]> git.sur5r.net Git - ngadmin/blobdiff - raw/src/attr.c
Fix network configuration modification
[ngadmin] / raw / src / attr.c
index 39f8f25cef937c33b24ae52a07f5ecc70e68e26c..aec75843bd1ad4af4e04a4283614924e918bcc62 100644 (file)
 
-#include <ngadmin.h>
+#include <stdarg.h>
+#include <errno.h>
 
-#include "attr.h"
-#include "protocol.h"
+#include <ngadmin.h> /* FIXME */
+#include <nsdp/attr.h>
+#include <nsdp/protocol.h>
 
 
-#define ATTR_HANDLER_ENTRY(at, sz, enc, dec)   {.attr = at, .size = sz, .encode = enc, .decode = dec}
+struct attr* newAttr (unsigned short attr, unsigned short size, void *data)
+{
+       struct attr *at;
+       
+       at = malloc(sizeof(struct attr));
+       if (at == NULL)
+               return NULL;
+       
+       at->attr = attr;
+       at->size = size;
+       at->data = data;
+       
+       return at;
+}
+
 
+void freeAttr (struct attr *at)
+{
+       if (at != NULL) {
+               free(at->data);
+               free(at);
+       }
+}
 
 
-static bool bool_endecode (struct attr *at, unsigned char ports UNUSED)
+void filterAttributes (List *attr, ...)
 {
-       *(char*)at->data = (*(char*)at->data != 0);
+       va_list ap;
+       ListNode *ln, *pr;
+       struct attr *at;
+       unsigned short attrcode;
+       bool keep;
        
-       return true;
+       
+       for (ln = attr->first; ln != NULL; ) {
+               at = ln->data;
+               
+               va_start(ap, attr);
+               keep = false;
+               attrcode = 0;
+               while (!keep && attrcode != ATTR_END) {
+                       attrcode = (unsigned short)va_arg(ap, unsigned int);
+                       keep = keep || (at->attr == attrcode);
+               }
+               va_end(ap);
+               
+               if (keep) {
+                       ln = ln->next;
+               } else {
+                       pr = ln;
+                       ln = ln->next;
+                       destroyElement(attr, pr, (void(*)(void*))freeAttr);
+               }
+       }
 }
 
 
-static bool ports_status_decode (struct attr *at, unsigned char ports)
+static int ports_status_endecode (struct attr *at)
 {
        struct attr_port_status *ps = at->data;
        
-       if (ps->port < 1 || ps->port > ports)
-               return false;
+       if (at->size != sizeof(struct attr_port_status))
+               return -EMSGSIZE;
+       
+       if (ps->port < 1)
+               return -EINVAL;
        
        switch (ps->status) {
+       
        case SPEED_DOWN:
        case SPEED_10:
        case SPEED_100:
        case SPEED_1000:
-               break;
+               return 0;
+       
        default:
-               return false;
+               return -EINVAL;
        }
-       
-       return true;
 }
 
 
-static bool port_stat_decode (struct attr *at, unsigned char ports)
+static int port_stat_endecode (struct attr *at, bool encode)
 {
        struct attr_port_stat *ps = at->data;
        unsigned long long *v;
        
-       if (ps->port < 1 || ps->port > ports)
-               return false;
+       if (at->size != sizeof(struct attr_port_stat))
+               return -EMSGSIZE;
        
-       for (v = &ps->recv; ((char*)v) - ((char*)ps) < (int)sizeof(struct attr_port_stat); v++)
-               *v = be64toh(*v);
+       if (ps->port < 1)
+               return -EINVAL;
        
+       for (v = &ps->recv; ((void*)v) - ((void*)ps) < (int)sizeof(struct attr_port_stat); v++)
+               *v = encode ? htobe64(*v) : be64toh(*v);
        
-       return true;
+       return 0;
 }
 
 
-static bool bitrate_decode (struct attr *at, unsigned char ports)
+static int qos_endecode (struct attr *at)
 {
-       struct attr_bitrate *sb = at->data;
+       struct attr_qos *aq = at->data;
        
-       if (sb->port < 1 || sb->port > ports)
-               return false;
+       if (at->size != sizeof(struct attr_qos))
+               return -EMSGSIZE;
        
-       sb->bitrate = ntohl(sb->bitrate);
-       if (sb->bitrate < BITRATE_UNSPEC || sb->bitrate > BITRATE_512M)
-               return false;
+       if (aq->port < 1)
+               return -EINVAL;
+       
+       if (aq->prio < PRIO_HIGH || aq->prio > PRIO_LOW)
+               return -EINVAL;
        
-       return true;
+       return 0;
 }
 
 
-static bool bitrate_encode (struct attr *at, unsigned char ports)
+static int bitrate_endecode (struct attr *at, bool encode)
 {
        struct attr_bitrate *sb = at->data;
        
-       if (sb->port < 1 || sb->port > ports)
-               return false;
+       if (at->size != sizeof(struct attr_bitrate))
+               return -EMSGSIZE;
+       
+       if (sb->port < 1)
+               return -EINVAL;
+       
+       if (!encode)
+               sb->bitrate = ntohl(sb->bitrate);
        
        if (sb->bitrate < BITRATE_UNSPEC || sb->bitrate > BITRATE_512M)
-               return false;
-       sb->bitrate = htonl(sb->bitrate);
+               return -EINVAL;
+       
+       if (encode)
+               sb->bitrate = htonl(sb->bitrate);
        
-       return true;
+       return 0;
 }
 
 
-static bool qos_mode_endecode (struct attr *at, unsigned char ports UNUSED)
+static int pvid_endecode (struct attr *at, bool encode)
 {
-       unsigned char v = *(char*)at->data;
+       struct attr_pvid *ap = at->data;
+       
+       if (at->size != sizeof(struct attr_pvid))
+               return -EMSGSIZE;
+       
+       if (ap->port < 1)
+               return -EINVAL;
        
-       return (v == QOS_PORT || v == QOS_DOT);
+       if (!encode)
+               ap->vlan = ntohs(ap->vlan);
+       
+       if (ap->vlan < VLAN_MIN || ap->vlan > VLAN_DOT_MAX)
+               return -EINVAL;
+       
+       if (encode)
+               ap->vlan = htons(ap->vlan);
+       
+       return 0;
 }
 
 
-static bool qos_endecode (struct attr *at, unsigned char ports)
+static int cabletest_do_endecode (struct attr *at)
 {
-       struct attr_qos *aq = at->data;
+       struct attr_cabletest_do *acd = at->data;
        
-       if (aq->port < 1 || aq->port > ports)
-               return false;
+       if (at->size != sizeof(struct attr_cabletest_do))
+               return -EMSGSIZE;
        
-       return (aq->prio >= PRIO_HIGH && aq->prio <= PRIO_LOW);
+       if (acd->port < 1 || acd->action != 1)
+               return -EINVAL;
+       
+       return 0;
 }
 
 
-static bool pvid_decode (struct attr *at, unsigned char ports)
+static int cabletest_result_endecode (struct attr *at, bool encode)
 {
-       struct attr_pvid *ap= at->data;
-       
-       if (ap->port < 1 || ap->port > ports)
-               return false;
+       struct attr_cabletest_result *acr = at->data;
        
-       ap->vlan = ntohs(ap->vlan);
+       /* Note: this attribute is special
+        * - when sent by the client, it contains the port number whe want to
+        *   get cabletest results from
+        * - when the switch replies, it contains the actual test results data
+        */
+       
+       /* no need to check the size, the only possible value under 1
+        * is 0 and in that case the decoder is not called
+        */
+       if (acr->port < 1)
+               return -EINVAL;
+       
+       if (at->size == 1)
+               return 0;
+       
+       if (at->size != sizeof(struct attr_cabletest_result))
+               return -EMSGSIZE;
+       
+       if (encode) {
+               acr->v1 = htonl(acr->v1);
+               acr->v2 = htonl(acr->v2);
+       } else {
+               acr->v1 = ntohl(acr->v1);
+               acr->v2 = ntohl(acr->v2);
+       }
        
-       return (ap->vlan >= VLAN_MIN && ap->vlan <= VLAN_MAX);
+       return 0;
 }
 
 
-static bool pvid_encode (struct attr *at, unsigned char ports)
+static int igmp_vlan_endecode (struct attr *at, bool encode)
 {
-       struct attr_pvid *ap= at->data;
+       struct attr_igmp_vlan *aiv = at->data;
        
-       if (ap->port < 1 || ap->port > ports)
-               return false;
+       if (at->size != sizeof(struct attr_igmp_vlan))
+               return -EMSGSIZE;
        
-       if (ap->vlan < VLAN_MIN || ap->vlan > VLAN_MAX)
-               return false;
+       if (!encode) {
+               aiv->enable = ntohs(aiv->enable);
+               aiv->vlan = ntohs(aiv->vlan);
+       }
+       
+       aiv->enable = (aiv->enable != 0);
+       if (aiv->vlan < VLAN_MIN || aiv->vlan > VLAN_DOT_MAX)
+               return -EINVAL;
        
-       ap->vlan = htons(ap->vlan);
+       if (encode) {
+               aiv->enable = htons(aiv->enable);
+               aiv->vlan = htons(aiv->vlan);
+       }
        
-       return true;
+       return 0;
 }
 
 
-static bool vlan_destroy_encode (struct attr *at, unsigned char ports UNUSED)
+static int mirror_encode (struct attr *at)
 {
-       unsigned short v = *(unsigned short*)at->data;
+       struct attr_mirror *am = at->data;
+       unsigned char p, ports, *r;
+       unsigned int sz;
+       
+       
+       /* no need to check attribute size, since
+        * sizeof(struct attr_mirror) == 1 and encoder is not
+        * called when attribute size is zero (ie empty attribute)
+        *
+        * am->outport encodes the outgoing mirror port and the array
+        * the ports from which the data is copied, it must not be empty
+        *
+        * am->outport == 0 is allowed and means mirroring is disabled
+        * but in that case the ports array must be empty
+        */
+       ports = at->size - sizeof(struct attr_mirror);
+       if ((am->outport == 0) ^ (ports == 0))
+               return -EINVAL;
+       
+       sz = 3 + ((ports - 1) >> 3);
+       r = malloc(sz);
+       if (r == NULL)
+               return -ENOMEM;
+       memset(r, 0, sz);
+       
+       r[0] = am->outport;
        
-       if (v < VLAN_MIN || v > VLAN_MAX)
-               return false;
+       /* FIXME: if ports > 8 */
+       for (p = 1; p <= ports; p++) {
+               if (am->outport != p)
+                       r[2] |= (am->ports[p] & 1) << (8 - p);
+       }
        
-       *(unsigned short*)at->data = htons(v);
+       free(at->data);
+       at->data = r;
+       at->size = sz;
        
-       return true;
+       
+       return 0;
 }
 
 
-static bool mirror_decode (struct attr *at, unsigned char ports)
+static int mirror_decode (struct attr *at)
 {
-       unsigned char *r = at->data, *p;
-       int port;
-       
+       struct attr_mirror *am;
+       unsigned char p, ports, *r = at->data;
+       unsigned int sz;
        
-       if (at->size != 3 + ((ports - 1) >> 3))
-               return false;
        
-       /* r[0] == 0 is allowed and means mirroring is disabled */
-       if (r[0] > ports)
-               return false;
-       
-       p = malloc(1 + ports);
-       if (p == NULL)
-               return false;
+       if (at->size < 3)
+               return -EMSGSIZE;
        
-       memset(p, 0, 1 + ports);
+       /* note: we cannot compute the exact amount of ports from here,
+        * instead we have the immediate superior multiple of 8
+        * it will be the user's job to ignore extra entries
+        */
+       ports = ((at->size - 2) << 3);
        
+       /* r[0] == 0 is allowed and means mirroring is disabled */
        if (r[0] == 0)
-               goto end;
+               ports = 0;
+       sz = sizeof(struct attr_mirror) + ports;
        
-       p[0] = r[0];
+       am = malloc(sz);
+       if (am == NULL)
+               return -ENOMEM;
+       memset(am, 0, sz);
        
-       for (port = 1; port <= ports; port++)
-               p[port] = (r[2] >> (8 - port)) & 1; /* FIXME: if ports > 8 */
+       am->outport = r[0];
+       
+       /* FIXME: if ports > 8 */
+       for (p = 1; p <= ports; p++)
+               am->ports[p] = (r[2] >> (8 - p)) & 1;
        
-end:
        free(at->data);
-       at->data = p;
-       at->size = 1 + ports;
+       at->data = am;
+       at->size = sz;
        
        
-       return true;
+       return 0;
 }
 
 
-static bool mirror_encode (struct attr *at, unsigned char ports)
+static int vlan_port_encode (struct attr *at)
 {
-       unsigned char *p = at->data, *r;
-       int port;
-       
+       struct attr_vlan_conf *avc = at->data;
+       unsigned char p, ports, *r;
+       unsigned int sz;
        
-       if (at->size != 1 + ports)
-               return false;
        
-       /* p[0] == 0 is allowed and means mirroring is disabled */
-       if (p[0] > ports)
-               return false;
+       /* just a header is valid */
+       if (at->size < sizeof(struct attr_vlan_conf))
+               return -EMSGSIZE;
+       ports = at->size - sizeof(struct attr_vlan_conf);
        
-       r = malloc(3 + ((ports - 1) >> 3));
-       if (r == NULL)
-               return false;
+       if (avc->vlan < VLAN_MIN || avc->vlan > VLAN_PORT_MAX)
+               return -EINVAL;
        
-       memset(r, 0, 3 + ((ports - 1) >> 3));
+       if (ports == 0)
+               sz = 2;
+       else
+               sz = (2 + 1 + ((ports - 1) >> 3));
        
-       if (p[0] == 0)
-               goto end;
+       r = malloc(sz);
+       if (r == NULL)
+               return -ENOMEM;
        
-       r[0] = p[0];
+       memset(r, 0, sz);
+       *(unsigned short*)r = htons(avc->vlan);
        
-       for (port = 1; port <= ports; port++) {
-               if (p[0] != port)
-                       r[2] |= (p[port] & 1) << (8 - port); /* FIXME: if ports > 8 */
+       /* FIXME: if ports > 8 */
+       for (p = 0; p < ports; p++) {
+               if (avc->ports[p] == VLAN_UNTAGGED)
+                       r[2] |= (1 << (7 - p));
        }
        
-end:
        free(at->data);
        at->data = r;
-       at->size = 3 + ((ports - 1) >> 3);
+       at->size = sz;
        
        
-       return true;
+       return 0;
 }
 
 
-static bool igmp_vlan_decode (struct attr *at, unsigned char ports UNUSED)
+static int vlan_port_decode (struct attr *at)
 {
-       struct attr_igmp_vlan *aiv = at->data;
+       unsigned char p, ports, *r = at->data;
+       struct attr_vlan_conf *avc;
+       unsigned int sz;
        
-       aiv->enable = ntohs(aiv->enable);
-       if (aiv->enable != 0 && aiv->enable != 1)
-               return false;
        
-       aiv->vlan = ntohs(aiv->vlan);
-       if (aiv->vlan < VLAN_MIN || aiv->vlan > VLAN_MAX)
-               return false;
+       if (at->size < 2)
+               return -EMSGSIZE;
        
-       return true;
-}
-
-
-static bool igmp_vlan_encode (struct attr *at, unsigned char ports UNUSED)
-{
-       struct attr_igmp_vlan *aiv = at->data;
+       /* note: we cannot compute the exact amount of ports from here,
+        * instead we have the immediate superior multiple of 8
+        * it will be the user's job to ignore extra entries
+        */
+       ports = ((at->size - 2) << 3);
        
-       if (aiv->enable != 0 && aiv->enable != 1)
-               return false;
-       aiv->enable = htons(aiv->enable);
+       sz = sizeof(struct attr_vlan_conf) + ports;
+       avc = malloc(sz);
+       if (avc == NULL)
+               return -ENOMEM;
        
-       if (aiv->vlan < VLAN_MIN || aiv->vlan > VLAN_MAX)
-               return false;
-       aiv->vlan = htons(aiv->vlan);
+       avc->vlan = ntohs(*(unsigned short*)r);
        
-       return true;
-}
-
-
-static bool cabletest_do_encode (struct attr *at, unsigned char ports)
-{
-       struct attr_cabletest_do *acd = at->data;
+       /* FIXME: if ports > 8 */
+       for (p = 0; p < ports; p++) {
+               if ((r[2] >> (7 - p)) & 1)
+                       avc->ports[p] = VLAN_UNTAGGED;
+               else
+                       avc->ports[p] = VLAN_NO;
+       }
        
-       if (acd->port < 1 || acd->port > ports)
-               return false;
+       free(at->data);
+       at->data = avc;
+       at->size = sz;
        
-       return (acd->action == 1);
-}
-
-
-static bool cabletest_result_encode (struct attr *at, unsigned char ports)
-{
-       unsigned char v = *(unsigned char*)at->data;
        
-       return (v >= 1 && v <= ports);
+       return 0;
 }
 
 
-static bool cabletest_result_decode (struct attr *at, unsigned char ports)
+static int vlan_dot_encode (struct attr *at)
 {
-       struct attr_cabletest_result *acr = at->data;
+       struct attr_vlan_conf *avc = at->data;
+       unsigned char p, ports, *r, fl;
+       unsigned int sz;
+       
        
-       if (acr->port < 1 || acr->port > ports)
-               return false;
+       /* just a header is valid */
+       if (at->size < sizeof(struct attr_vlan_conf))
+               return -EMSGSIZE;
+       ports = at->size - sizeof(struct attr_vlan_conf);
        
-       acr->v1 = ntohl(acr->v1);
-       acr->v2 = ntohl(acr->v2);
+       if (avc->vlan < VLAN_MIN || avc->vlan > VLAN_DOT_MAX)
+               return -EINVAL;
        
-       return true;
-}
-
-
-static bool cabletest_result_endecode (struct attr *at, unsigned char ports)
-{
-       switch (at->size) {
+       if (ports == 0)
+               sz = 2;
+       else
+               sz = 2 + 2 * (1 + ((ports - 1) >> 3));
        
-       case 1:
-               return cabletest_result_encode(at, ports);
+       r = malloc(sz);
+       if (r == NULL)
+               return -EMSGSIZE;
        
-       case sizeof(struct attr_cabletest_result):
-               return cabletest_result_decode(at, ports);
+       memset(r, 0, sz);
+       *(unsigned short*)r = htons(avc->vlan);
        
-       default:
-               return false;
+       /* FIXME: if ports > 8 */
+       for (p = 0; p < ports; p++) {
+               fl = (1 << (7 - p));
+               switch (avc->ports[p]) {
+               case VLAN_TAGGED:
+                       r[3] |= fl;
+                       /* a tagged VLAN is also marked as untagged
+                        * so do not put a "break" here
+                        */
+               case VLAN_UNTAGGED:
+                       r[2] |= fl;
+               }
        }
+       
+       
+       free(at->data);
+       at->data = r;
+       at->size = sz;
+       
+       
+       return 0;
 }
 
 
-static bool vlan_type_endecode (struct attr *at, unsigned char ports UNUSED)
+static int vlan_dot_decode (struct attr *at)
 {
-       char v = *(char*)at->data;
+       unsigned char p, ports, *r = at->data;
+       struct attr_vlan_conf *avc;
+       unsigned int sz;
+       
+       
+       /* attribute size must be a multiple of 2 because there are
+        * 2 bytes (1 for tagged and 1 for untagged) per block of
+        * 8 ports, plus the 2 first bytes for the VLAN id
+        */
+       if (at->size < 2 || (at->size & 1) != 0)
+               return -EMSGSIZE;
+       
+       /* note: we cannot compute the exact amount of ports from here,
+        * instead we have the immediate superior multiple of 8
+        * it will be the user's job to ignore extra entries
+        */
+       ports = ((at->size - 2) / 2) << 3;
+       
+       sz = sizeof(struct attr_vlan_conf) + ports;
+       avc = malloc(sz);
+       if (avc == NULL)
+               return -ENOMEM;
+       
+       avc->vlan = ntohs(*(unsigned short*)r);
+       
+       /* FIXME: if ports > 8 */
+       for (p = 0; p < ports; p++) {
+               if ((r[3] >> (7 - p)) & 1)
+                       avc->ports[p] = VLAN_TAGGED;
+               else if ((r[2] >> (7 - p)) & 1)
+                       avc->ports[p] = VLAN_UNTAGGED;
+               else
+                       avc->ports[p] = VLAN_NO;
+       }
+       
+       free(at->data);
+       at->data = avc;
+       at->size = sz;
+       
        
-       return (v >= VLAN_DISABLED && v <= VLAN_DOT_ADV);
+       return 0;
 }
 
 
-static bool vlan_dot_decode (struct attr *at, unsigned char ports)
+static int processAttr (struct attr *at, bool encode)
 {
-       char *r = at->data;
-       struct attr_vlan_dot *avd;
-       int port;
+       unsigned char *byte = at->data;
+       unsigned short *word = at->data;
+       unsigned int *dword = at->data;
        
        
-       if (at->size != (2 + 2 * (1 + ((ports - 1) >> 3))))
-               return false;
+       /* empty attributes are not processed */
+       if (at->size == 0)
+               return 0;
        
-       avd = malloc(sizeof(struct attr_vlan_dot) + ports);
-       if (avd == NULL)
-               return false;
+       if (at->data == NULL)
+               return -EFAULT;
        
-       avd->vlan = ntohs(*(unsigned short*)r);
-       r += 2;
+       switch (at->attr) {
        
-       for (port = 0; port < ports; port++) {
-               /* FIXME: if ports > 8 */
-               if ((r[1] >> (7 - port)) & 1)
-                       avd->ports[port] = VLAN_TAGGED;
-               else if ((r[0] >> (7 - port)) & 1)
-                       avd->ports[port] = VLAN_UNTAGGED;
-               else
-                       avd->ports[port] = VLAN_NO;
-       }
+       case ATTR_MAC:
+               if (at->size != ETH_ALEN)
+                       return -EMSGSIZE;
+               return 0;
        
-       free(at->data);
-       at->data = avd;
-       at->size = sizeof(struct attr_vlan_dot) + ports;
+       case ATTR_IP:
+       case ATTR_NETMASK:
+       case ATTR_GATEWAY:
+               /* IP addresses are kept in network byte order even on the host */
+               if (at->size != sizeof(struct in_addr))
+                       return -EMSGSIZE;
+               
+               return 0;
        
+       case ATTR_PORTS_COUNT:
+               if (at->size != 1)
+                       return -EMSGSIZE;
+               
+               return 0;
+       
+       case ATTR_RESTART:
+       case ATTR_DEFAULTS:
+       case ATTR_STATS_RESET:
+       case ATTR_STORM_ENABLE:
+       case ATTR_IGMP_BLOCK_UNK:
+       case ATTR_IGMP_VALID_V3:
+               if (at->size != 1)
+                       return -EMSGSIZE;
+               
+               *byte = (*byte != 0);
+               
+               return 0;
+       
+       case ATTR_DHCP:
+               /* Note: DHCP attribute is special, it is 2 two bytes long
+                * when sent by the switch but only 1 byte long when sent
+                * by the client
+                */
+               if (at->size == 1) {
+                       *byte = (*byte != 0);
+                       return 0;
+               } else if (at->size > 2) {
+                       return -EMSGSIZE;
+               }
+               
+               if (!encode)
+                       *word = ntohs(*word);
+               
+               *word = (*word != 0);
+               
+               if (encode)
+                       *word = htons(*word);
+               
+               return 0;
        
-       return true;
-}
-
-
-static bool vlan_dot_encode (struct attr *at, unsigned char ports)
-{
-       struct attr_vlan_dot *avd = at->data;
-       char *r, fl;
-       unsigned int size, port;
+       case ATTR_ENCPASS:
+               if (at->size != 4)
+                       return -EMSGSIZE;
+               
+               if (!encode)
+                       *dword = ntohl(*dword);
+               
+               *dword = (*dword != 0);
+               
+               if (encode)
+                       *dword = htonl(*dword);
+               
+               return 0;
        
+       case ATTR_VLAN_TYPE:
+               if (at->size != 1)
+                       return -EMSGSIZE;
+               
+               /* no need to check if *byte < VLAN_DISABLED because
+                * byte is unsigned and VLAN_DISABLED is 0 */
+               if (*byte > VLAN_DOT_ADV)
+                       return -EINVAL;
+               
+               return 0;
        
-       if (avd->vlan < VLAN_MIN || avd->vlan > VLAN_MAX)
-               return false;
+       case ATTR_QOS_TYPE:
+               if (at->size != 1)
+                       return -EMSGSIZE;
+               
+               if (*byte < QOS_PORT || *byte > QOS_DOT)
+                       return -EINVAL;
+               
+               return 0;
        
-       /* just a header is valid */
-       if (at->size == sizeof(struct attr_vlan_dot))
-               size = 2;
-       else if (at->size == sizeof(struct attr_vlan_dot) + ports)
-               size = (2 + 2 * (1 + ((ports - 1) >> 3)));
-       else
-               return false;
+       case ATTR_QOS_CONFIG:
+               return qos_endecode(at);
        
-       r = malloc(size);
-       if (r == NULL)
-               return false;
+       case ATTR_VLAN_DESTROY:
+               if (at->size != 2)
+                       return -EMSGSIZE;
+               
+               if (!encode)
+                       *word = ntohs(*word);
+               
+               if (*word < VLAN_MIN || *word > VLAN_DOT_MAX)
+                       return -EINVAL;
+               
+               if (encode)
+                       *word = htons(*word);
+               
+               return 0;
        
-       memset(r, 0, size);
-       *(unsigned short*)r = htons(avd->vlan);
+       case ATTR_PORT_STATUS:
+               return ports_status_endecode(at);
        
-       if (size == 2)
-               goto end;
+       case ATTR_PORT_STATISTICS:
+               return port_stat_endecode(at, encode);
        
-       r += 2;
+       case ATTR_BITRATE_INPUT:
+       case ATTR_BITRATE_OUTPUT:
+       case ATTR_STORM_BITRATE:
+               return bitrate_endecode(at, encode);
        
-       for (port = 0; port < ports; port++) {
-               /* FIXME: if ports > 8 */
-               fl = (1 << (7 - port));
-               switch (avd->ports[port]) {
-               case VLAN_TAGGED:
-                       r[1] |= fl;
-               case VLAN_UNTAGGED:
-                       r[0] |= fl;
-               }
-       }
+       case ATTR_VLAN_PVID:
+               return pvid_endecode(at, encode);
        
-       r -= 2;
+       case ATTR_CABLETEST_DO:
+               return cabletest_do_endecode(at);
        
-end:
-       free(at->data);
-       at->data = r;
-       at->size = size;
+       case ATTR_CABLETEST_RESULT:
+               return cabletest_result_endecode(at, encode);
        
+       case ATTR_IGMP_ENABLE_VLAN:
+               return igmp_vlan_endecode(at, encode);
        
-       return true;
+       case ATTR_MIRROR:
+               if (encode)
+                       return mirror_encode(at);
+               else
+                       return mirror_decode(at);
+       
+       case ATTR_VLAN_PORT_CONF:
+               if (encode)
+                       return vlan_port_encode(at);
+               else
+                       return vlan_port_decode(at);
+       
+       case ATTR_VLAN_DOT_CONF:
+               if (encode)
+                       return vlan_dot_encode(at);
+               else
+                       return vlan_dot_decode(at);
+       
+       /* undefined attributes are not modified */
+       default:
+               return 0;
+       }
 }
 
 
+int encodeAttr (struct attr *at)
+{
+       return processAttr(at, true);
+}
 
-/* WARNING: attributes codes MUST be in ascending order */
-static const struct attr_handler attrtab[] = {
-       ATTR_HANDLER_ENTRY(ATTR_MAC, 6, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_IP, 4, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_NETMASK, 4, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_GATEWAY, 4, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_DHCP, 2, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_RESTART, 1, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_DEFAULTS, 1, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_PORT_STATUS, sizeof(struct attr_port_status), NULL, ports_status_decode),
-       ATTR_HANDLER_ENTRY(ATTR_PORT_STATISTICS, sizeof(struct attr_port_stat), NULL, port_stat_decode),
-       ATTR_HANDLER_ENTRY(ATTR_STATS_RESET, 1, bool_endecode, bool_endecode),
-       ATTR_HANDLER_ENTRY(ATTR_CABLETEST_DO, sizeof(struct attr_cabletest_do), cabletest_do_encode, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_CABLETEST_RESULT, 0, cabletest_result_endecode, cabletest_result_endecode),
-       ATTR_HANDLER_ENTRY(ATTR_VLAN_TYPE, 1, vlan_type_endecode, vlan_type_endecode),
-       ATTR_HANDLER_ENTRY(ATTR_VLAN_DOT_CONF, 0, vlan_dot_encode, vlan_dot_decode),
-       ATTR_HANDLER_ENTRY(ATTR_VLAN_DESTROY, 2, vlan_destroy_encode, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_VLAN_PVID, sizeof(struct attr_pvid), pvid_encode, pvid_decode),
-       ATTR_HANDLER_ENTRY(ATTR_QOS_TYPE, 1, qos_mode_endecode, qos_mode_endecode),
-       ATTR_HANDLER_ENTRY(ATTR_QOS_CONFIG, sizeof(struct attr_qos), qos_endecode, qos_endecode),
-       ATTR_HANDLER_ENTRY(ATTR_BITRATE_INPUT, sizeof(struct attr_bitrate), bitrate_encode, bitrate_decode),
-       ATTR_HANDLER_ENTRY(ATTR_BITRATE_OUTPUT, sizeof(struct attr_bitrate), bitrate_encode, bitrate_decode),
-       ATTR_HANDLER_ENTRY(ATTR_STORM_ENABLE, 1, bool_endecode, bool_endecode),
-       ATTR_HANDLER_ENTRY(ATTR_STORM_BITRATE, sizeof(struct attr_bitrate), bitrate_encode, bitrate_decode),
-       ATTR_HANDLER_ENTRY(ATTR_MIRROR, 0, mirror_encode, mirror_decode),
-       ATTR_HANDLER_ENTRY(ATTR_PORTS_COUNT, 1, NULL, NULL),
-       ATTR_HANDLER_ENTRY(ATTR_IGMP_ENABLE_VLAN, sizeof(struct attr_igmp_vlan), igmp_vlan_encode, igmp_vlan_decode),
-       ATTR_HANDLER_ENTRY(ATTR_IGMP_BLOCK_UNK, 1, bool_endecode, bool_endecode),
-       ATTR_HANDLER_ENTRY(ATTR_IGMP_VALID_V3, 1, bool_endecode, bool_endecode)
-};
-
-
-
-const struct attr_handler* getAttrHandler (unsigned short attrcode)
+
+int decodeAttr (struct attr *at)
 {
-       const struct attr_handler *ah;
-       const unsigned int tab_size = sizeof(attrtab) / sizeof(struct attr_handler);
-       unsigned int inf, sup, index;
-       
-       
-       inf = 0;
-       sup = tab_size;
-       index = tab_size >> 1;
-       while (index < sup) {
-               ah = &attrtab[index];
-               
-               if (ah->attr > attrcode) {
-                       sup = index;
-                       index -= ((index - inf) >> 1) + ((index - inf) & 1);
-               } else if (ah->attr < attrcode) {
-                       inf = index;
-                       index += ((sup - index) >> 1) + ((sup - index) & 1);
-               } else {
-                       return ah;
-               }
-       }
-       
-       
-       return NULL;
+       return processAttr(at, false);
 }