From 94083675e38056ea736ef2e6e69fcef6acc6e5fc Mon Sep 17 00:00:00 2001 From: darkcoven Date: Sun, 22 Sep 2013 01:52:59 +0200 Subject: [PATCH] Add support for port based VLANs --- cli/com_vlan.c | 127 +++++++++++++++++++++++++++++++++-- cli/commands.c | 6 +- lib/include/ngadmin.h | 34 +++++++++- lib/src/ngadmin.c | 153 ++++++++++++++++++++++++++++++++++++------ raw/include/attr.h | 2 +- raw/src/attr.c | 125 ++++++++++++++++++++++++++++------ 6 files changed, 399 insertions(+), 48 deletions(-) diff --git a/cli/com_vlan.c b/cli/com_vlan.c index 1e2b573..7c911bc 100644 --- a/cli/com_vlan.c +++ b/cli/com_vlan.c @@ -41,7 +41,7 @@ int do_vlan_8021q_del (int argc, const char **argv, struct ngadmin *nga) } vlan=strtoul(argv[0], NULL, 0); - if (vlan < 1 || vlan > VLAN_MAX) { + if (vlan < VLAN_MIN || vlan > VLAN_DOT_MAX) { printf("vlan out of range\n"); return 1; } @@ -54,6 +54,125 @@ int do_vlan_8021q_del (int argc, const char **argv, struct ngadmin *nga) } +int do_vlan_port_set (int argc, const char **argv, struct ngadmin *nga) +{ + unsigned char vlan, port, *ports = NULL; + const struct swi_attr *sa; + int i, k = 0, ret = 0; + + + if (argc < 2) { + printf("usage: vlan port set [all ] [ ] [ ] [...]\n"); + ret = 1; + goto end; + } + + sa = ngadmin_getCurrentSwitch(nga); + if (sa == NULL) { + printf("must be logged\n"); + ret = 1; + goto end; + } + + ports = malloc(sa->ports * sizeof(unsigned char)); + + /* read defaults */ + port = 0; + if (strcmp(argv[k], "all") == 0) { + k++; + port = strtoul(argv[k++], NULL, 0); + if (port < 1 || port > sa->ports) { + printf("port out of range"); + ret = 1; + goto end; + } + } + + /* apply defaults */ + memset(ports, port, sa->ports); + + /* read and apply port specifics */ + while (k < argc - 1) { + /* read port */ + port = strtoul(argv[k++], NULL, 0); + if (port < 1 || port > sa->ports) { + printf("port out of range"); + ret = 1; + goto end; + } + + /* read vlan */ + vlan = strtoul(argv[k++], NULL, 0); + if (vlan < VLAN_MIN || vlan > VLAN_PORT_MAX) { + printf("vlan out of range\n"); + ret = 1; + goto end; + } + + ports[port - 1] = vlan; + } + + /* set conf */ + i = ngadmin_setVLANPortConf(nga, ports); + printErrCode(i); + +end: + free(ports); + + return ret; +} + + +int do_vlan_port_show (int argc, const char **argv UNUSED, struct ngadmin *nga) +{ + unsigned char *ports = NULL; + const struct swi_attr *sa; + int i, ret = 0; + + + if (argc > 0) { + printf("this command takes no argument\n"); + ret = 1; + goto end; + } + + sa = ngadmin_getCurrentSwitch(nga); + if (sa == NULL) { + printf("must be logged\n"); + ret = 1; + goto end; + } + + ports = malloc(sa->ports * sizeof(unsigned char)); + + /* request all VLANs config */ + i = ngadmin_getVLANPortConf(nga, ports); + + if (i != ERR_OK) { + printErrCode(i); + ret = 1; + goto end; + } + + printf("Ports configuration: \n"); + printf("Port\t"); + for (i = 1; i <= sa->ports; i++) + printf("%i\t", i); + putchar('\n'); + + /* show all VLANs */ + printf("VLAN\t"); + for (i = 0; i < sa->ports; i++) + printf("%u\t", ports[i]); + putchar('\n'); + +end: + free(ports); + + return ret; +} + + int do_vlan_8021q_set (int argc, const char **argv, struct ngadmin *nga) { unsigned char *ports = NULL, p, def = VLAN_UNSPEC; @@ -78,7 +197,7 @@ int do_vlan_8021q_set (int argc, const char **argv, struct ngadmin *nga) /* read vlan */ vlan = strtoul(argv[k++], NULL, 0); - if (vlan < 1 || vlan > VLAN_MAX) { + if (vlan < VLAN_MIN || vlan > VLAN_DOT_MAX) { printf("vlan out of range\n"); ret = 1; goto end; @@ -108,7 +227,7 @@ int do_vlan_8021q_set (int argc, const char **argv, struct ngadmin *nga) /* apply defaults */ memset(ports, def, sa->ports); - /* apply port specifics */ + /* read and apply port specifics */ while (k < argc - 1) { p = strtoul(argv[k++], NULL, 0) - 1; if (p >= sa->ports) { @@ -329,7 +448,7 @@ int do_vlan_pvid_set (int argc, const char **argv, struct ngadmin *nga) return 1; } - if (vlan < 1 || vlan > VLAN_MAX) { + if (vlan < VLAN_MIN || vlan > VLAN_DOT_MAX) { printf("vlan out of range\n"); return 1; } diff --git a/cli/commands.c b/cli/commands.c index 6d6bc2a..2d37ea8 100644 --- a/cli/commands.c +++ b/cli/commands.c @@ -95,6 +95,8 @@ int do_tree (int argc, const char **argv, struct ngadmin *nga); /* vlan */ int do_vlan_8021q_del (int argc, const char **argv, struct ngadmin *nga); +int do_vlan_port_set (int argc, const char **argv, struct ngadmin *nga); +int do_vlan_port_show (int argc, const char **argv, struct ngadmin *nga); int do_vlan_8021q_set (int argc, const char **argv, struct ngadmin *nga); int do_vlan_8021q_show (int argc, const char **argv, struct ngadmin *nga); int do_vlan_mode_set (int argc, const char **argv, struct ngadmin *nga); @@ -191,8 +193,8 @@ COM_ROOT_START(commands) COM_TERM(show, do_vlan_mode_show) COM_END COM_START(port) - COM_TERM(set, NULL) - COM_TERM(show, NULL) + COM_TERM(set, do_vlan_port_set) + COM_TERM(show, do_vlan_port_show) COM_END COM_START(pvid) COM_TERM(set, do_vlan_pvid_set) diff --git a/lib/include/ngadmin.h b/lib/include/ngadmin.h index d4c7685..43bcf12 100644 --- a/lib/include/ngadmin.h +++ b/lib/include/ngadmin.h @@ -106,10 +106,14 @@ enum { #define VLAN_MIN 1 /** - * Maximum VLAN id. + * Maximum 802.1q VLAN id. **/ -#define VLAN_MAX 4093 +#define VLAN_DOT_MAX 4093 +/** + * Maximum port VLAN id. + **/ +#define VLAN_PORT_MAX 9 /** @@ -685,6 +689,32 @@ int ngadmin_getVLANType (struct ngadmin *nga, int *t) EXPORT; int ngadmin_setVLANType (struct ngadmin *nga, int t) EXPORT; +/** + * Get the ports VLANs in port mode. + * Retrieves the associated VLAN of ports in port mode. + * @note The switch should be in port mode. + * @note You must be logged on a switch. + * @param nga A pointer to the ngadmin structure. + * @param ports A pointer to an array of integers which will receive the + number of associated VLAN. Must not be NULL. + * @return ERR_OK when everything is well or an error code otherwise. + **/ +int ngadmin_getVLANPortConf (struct ngadmin *nga, unsigned char *ports) EXPORT; + + +/** + * Set the ports VLAN in port mode. + * Changes the associated VLAN of ports in port mode. + * @note The switch should be in port mode. + * @note You must be logged on a switch. + * @param nga A pointer to the ngadmin structure. + * @param ports A pointer to an array of integers which contain the + number of associated VLAN. Must not be NULL. + * @return ERR_OK when everything is well or an error code otherwise. + **/ +int ngadmin_setVLANPortConf (struct ngadmin *nga, const unsigned char *ports) EXPORT; + + /** * Get all the 802.1q VLAN configuration. * Retrieves all the VLAN configuration in 802.1q mode. diff --git a/lib/src/ngadmin.c b/lib/src/ngadmin.c index 3156d02..165d0e4 100644 --- a/lib/src/ngadmin.c +++ b/lib/src/ngadmin.c @@ -1151,13 +1151,128 @@ int ngadmin_setVLANType (struct ngadmin *nga, int t) } +int ngadmin_getVLANPortConf (struct ngadmin *nga, unsigned char *ports) +{ + List *attr; + ListNode *ln; + struct attr *at; + int ret = ERR_OK; + struct attr_vlan_conf *avc; + struct swi_attr *sa; + int port; + + + if (nga == NULL || ports== NULL) + return ERR_INVARG; + + sa = nga->current; + if (sa == NULL) + return ERR_NOTLOG; + + + attr = createEmptyList(); + pushBackList(attr, newEmptyAttr(ATTR_VLAN_PORT_CONF)); + ret = readRequest(nga, attr); + if (ret != ERR_OK) + goto end; + + filterAttributes(attr, ATTR_VLAN_PORT_CONF, ATTR_END); + + memset(ports, 0, sa->ports); + + for (ln = attr->first; ln != NULL; ln = ln->next) { + at = ln->data; + avc = at->data; + + for (port = 0; port < sa->ports; port++) { + if (avc->ports[port] == VLAN_UNTAGGED) + ports[port] = avc->vlan; + } + } + + +end: + destroyList(attr, (void(*)(void*))freeAttr); + + + return ret; +} + + +int ngadmin_setVLANPortConf (struct ngadmin *nga, const unsigned char *ports) +{ + List *attr = NULL; + ListNode *ln; + struct attr *at; + struct swi_attr *sa; + struct attr_vlan_conf *avc; + int ret = ERR_OK, port; + unsigned char vlan; + + + if (nga == NULL || ports == NULL) + return ERR_INVARG; + + sa = nga->current; + if (sa == NULL) + return ERR_NOTLOG; + + /* if nothing is to be changed, do nothing */ + for (port = 0; port < sa->ports && ports[port] == 0; port++); + if (port == sa->ports ) + goto end; + + attr = createEmptyList(); + + if (memchr(ports, 0, sa->ports) != NULL) { + /* if at least one port is unchanged, we need to read old config */ + pushBackList(attr, newEmptyAttr(ATTR_VLAN_PORT_CONF)); + ret = readRequest(nga, attr); + if (ret != ERR_OK) + goto end; + + filterAttributes(attr, ATTR_VLAN_PORT_CONF, ATTR_END); + /* FIXME: check if the returned array effectively contains correct data */ + } else { + /* create an empty VLAN config */ + for (vlan = VLAN_MIN; vlan <= VLAN_PORT_MAX; vlan++) { + avc = malloc(sizeof(struct attr_vlan_conf) + sa->ports); + avc->vlan = vlan; + memset(avc->ports, 0, sa->ports); + pushBackList(attr, newAttr(ATTR_VLAN_PORT_CONF, sizeof(struct attr_vlan_conf) + sa->ports, avc)); + } + } + + for (ln = attr->first; ln != NULL; ln = ln->next) { + at = ln->data; + avc = at->data; + for (port = 0; port < sa->ports; port++) { + if (ports[port] == avc->vlan) + avc->ports[port] = VLAN_UNTAGGED; + else + avc->ports[port] = VLAN_NO; + } + } + + ret = writeRequest(nga, attr); + attr = NULL; + + +end: + destroyList(attr, (void(*)(void*))freeAttr); + + + return ret; +} + + int ngadmin_getVLANDotAllConf (struct ngadmin *nga, unsigned short *vlans, unsigned char *ports, int *nb) { List *attr; ListNode *ln; struct attr *at; int ret = ERR_OK, total; - struct attr_vlan_dot *avd; + struct attr_vlan_conf *avc; if (nga == NULL || vlans == NULL || ports== NULL || nb == NULL || *nb <= 0) @@ -1182,10 +1297,10 @@ int ngadmin_getVLANDotAllConf (struct ngadmin *nga, unsigned short *vlans, unsig for (ln = attr->first; ln != NULL; ln = ln->next) { at = ln->data; - avd = at->data; + avc = at->data; - *vlans = avd->vlan; - memcpy(ports, avd->ports, nga->current->ports); + *vlans = avc->vlan; + memcpy(ports, avc->ports, nga->current->ports); vlans++; ports += nga->current->ports; @@ -1210,10 +1325,10 @@ int ngadmin_getVLANDotConf (struct ngadmin *nga, unsigned short vlan, unsigned c ListNode *ln; struct attr *at; int ret = ERR_OK; - struct attr_vlan_dot *avd; + struct attr_vlan_conf *avc; - if (nga == NULL || vlan < 1 || vlan > VLAN_MAX || ports == NULL) + if (nga == NULL || vlan < VLAN_MIN || vlan > VLAN_DOT_MAX || ports == NULL) return ERR_INVARG; else if (nga->current == NULL) return ERR_NOTLOG; @@ -1231,9 +1346,9 @@ int ngadmin_getVLANDotConf (struct ngadmin *nga, unsigned short vlan, unsigned c for (ln = attr->first; ln != NULL; ln = ln->next) { at = ln->data; - avd = at->data; - if (avd->vlan == vlan) { - memcpy(ports, avd->ports, nga->current->ports); + avc = at->data; + if (avc->vlan == vlan) { + memcpy(ports, avc->ports, nga->current->ports); break; } } @@ -1252,11 +1367,11 @@ int ngadmin_setVLANDotConf (struct ngadmin *nga, unsigned short vlan, const unsi List *attr = NULL; struct attr *at; struct swi_attr *sa; - struct attr_vlan_dot *avd; + struct attr_vlan_conf *avc; int ret = ERR_OK, port; - if (nga == NULL || vlan < 1 || vlan > VLAN_MAX || ports == NULL) + if (nga == NULL || vlan < VLAN_MIN || vlan > VLAN_DOT_MAX || ports == NULL) return ERR_INVARG; sa = nga->current; @@ -1271,11 +1386,11 @@ int ngadmin_setVLANDotConf (struct ngadmin *nga, unsigned short vlan, const unsi attr = createEmptyList(); - avd = malloc(sizeof(struct attr_vlan_dot) + sa->ports); - if (avd == NULL) + avc = malloc(sizeof(struct attr_vlan_conf) + sa->ports); + if (avc == NULL) return ERR_MEM; - avd->vlan = vlan; + avc->vlan = vlan; /* if all is to be changed, we do not need to read old config */ if (memchr(ports, VLAN_UNSPEC, sa->ports) != NULL) { @@ -1289,7 +1404,7 @@ int ngadmin_setVLANDotConf (struct ngadmin *nga, unsigned short vlan, const unsi if (attr->first != NULL) { at = attr->first->data; - memcpy(avd, at->data, sizeof(struct attr_vlan_dot) + sa->ports); + memcpy(avc, at->data, sizeof(struct attr_vlan_conf) + sa->ports); } clearList(attr, (void(*)(void*))freeAttr); @@ -1299,11 +1414,11 @@ int ngadmin_setVLANDotConf (struct ngadmin *nga, unsigned short vlan, const unsi /* apply changes */ for (port = 0; port < sa->ports; port++) { if (ports[port] != VLAN_UNSPEC) - avd->ports[port] = ports[port]; + avc->ports[port] = ports[port]; } - pushBackList(attr, newAttr(ATTR_VLAN_DOT_CONF, sizeof(struct attr_vlan_dot) + sa->ports, avd)); + pushBackList(attr, newAttr(ATTR_VLAN_DOT_CONF, sizeof(struct attr_vlan_conf) + sa->ports, avc)); ret = writeRequest(nga, attr); attr = NULL; @@ -1321,7 +1436,7 @@ int ngadmin_VLANDestroy (struct ngadmin *nga, unsigned short vlan) List *attr; - if (nga == NULL || vlan < 1 || vlan > VLAN_MAX) + if (nga == NULL || vlan < VLAN_MIN || vlan > VLAN_DOT_MAX) return ERR_INVARG; else if (nga->current == NULL) return ERR_NOTLOG; @@ -1381,7 +1496,7 @@ int ngadmin_setPVID (struct ngadmin *nga, unsigned char port, unsigned short vla struct attr_pvid *ap; - if (nga == NULL || port < 1 || vlan < 1 || vlan > VLAN_MAX) + if (nga == NULL || port < 1 || vlan < VLAN_MIN || vlan > VLAN_DOT_MAX) return ERR_INVARG; else if (nga->current == NULL) return ERR_NOTLOG; diff --git a/raw/include/attr.h b/raw/include/attr.h index 7f0c6e9..dfbdce0 100644 --- a/raw/include/attr.h +++ b/raw/include/attr.h @@ -123,7 +123,7 @@ struct attr_cabletest_result { } __attribute__((packed)); -struct attr_vlan_dot { +struct attr_vlan_conf { unsigned short vlan; unsigned char ports[0]; }; diff --git a/raw/src/attr.c b/raw/src/attr.c index e6ce0ca..4547713 100644 --- a/raw/src/attr.c +++ b/raw/src/attr.c @@ -112,7 +112,7 @@ static bool pvid_decode (struct attr *at, unsigned char ports) ap->vlan = ntohs(ap->vlan); - return (ap->vlan >= VLAN_MIN && ap->vlan <= VLAN_MAX); + return (ap->vlan >= VLAN_MIN && ap->vlan <= VLAN_DOT_MAX); } @@ -123,7 +123,7 @@ static bool pvid_encode (struct attr *at, unsigned char ports) if (ap->port < 1 || ap->port > ports) return false; - if (ap->vlan < VLAN_MIN || ap->vlan > VLAN_MAX) + if (ap->vlan < VLAN_MIN || ap->vlan > VLAN_DOT_MAX) return false; ap->vlan = htons(ap->vlan); @@ -136,7 +136,7 @@ static bool vlan_destroy_encode (struct attr *at, unsigned char ports UNUSED) { unsigned short v = *(unsigned short*)at->data; - if (v < VLAN_MIN || v > VLAN_MAX) + if (v < VLAN_MIN || v > VLAN_DOT_MAX) return false; *(unsigned short*)at->data = htons(v); @@ -230,7 +230,7 @@ static bool igmp_vlan_decode (struct attr *at, unsigned char ports UNUSED) return false; aiv->vlan = ntohs(aiv->vlan); - if (aiv->vlan < VLAN_MIN || aiv->vlan > VLAN_MAX) + if (aiv->vlan < VLAN_MIN || aiv->vlan > VLAN_DOT_MAX) return false; return true; @@ -245,7 +245,7 @@ static bool igmp_vlan_encode (struct attr *at, unsigned char ports UNUSED) return false; aiv->enable = htons(aiv->enable); - if (aiv->vlan < VLAN_MIN || aiv->vlan > VLAN_MAX) + if (aiv->vlan < VLAN_MIN || aiv->vlan > VLAN_DOT_MAX) return false; aiv->vlan = htons(aiv->vlan); @@ -310,36 +310,118 @@ static bool vlan_type_endecode (struct attr *at, unsigned char ports UNUSED) } +static bool vlan_port_decode (struct attr *at, unsigned char ports) +{ + char *r = at->data; + struct attr_vlan_conf *avc; + int port; + + + if (at->size != (2 + 1 + ((ports - 1) >> 3))) + return false; + + avc = malloc(sizeof(struct attr_vlan_conf) + ports); + if (avc == NULL) + return false; + + avc->vlan = ntohs(*(unsigned short*)r); + r += 2; + + for (port = 0; port < ports; port++) { + /* FIXME: if ports > 8 */ + if ((r[0] >> (7 - port)) & 1) + avc->ports[port] = VLAN_UNTAGGED; + else + avc->ports[port] = VLAN_NO; + } + + free(at->data); + at->data = avc; + at->size = sizeof(struct attr_vlan_conf) + ports; + + + return true; +} + + +static bool vlan_port_encode (struct attr *at, unsigned char ports) +{ + struct attr_vlan_conf *avc = at->data; + char *r; + unsigned int size, port; + + + if (avc->vlan < VLAN_MIN || avc->vlan > VLAN_PORT_MAX) + return false; + + /* just a header is valid */ + if (at->size == sizeof(struct attr_vlan_conf)) + size = 2; + else if (at->size == sizeof(struct attr_vlan_conf) + ports) + size = (2 + 1 + ((ports - 1) >> 3)); + else + return false; + + r = malloc(size); + if (r == NULL) + return false; + + memset(r, 0, size); + *(unsigned short*)r = htons(avc->vlan); + + if (size == 2) + goto end; + + r += 2; + + for (port = 0; port < ports; port++) { + /* FIXME: if ports > 8 */ + if (avc->ports[port] == VLAN_UNTAGGED) + r[0] |= (1 << (7 - port)); + } + + r -= 2; + +end: + free(at->data); + at->data = r; + at->size = size; + + + return true; +} + + static bool vlan_dot_decode (struct attr *at, unsigned char ports) { char *r = at->data; - struct attr_vlan_dot *avd; + struct attr_vlan_conf *avc; int port; if (at->size != (2 + 2 * (1 + ((ports - 1) >> 3)))) return false; - avd = malloc(sizeof(struct attr_vlan_dot) + ports); - if (avd == NULL) + avc = malloc(sizeof(struct attr_vlan_conf) + ports); + if (avc == NULL) return false; - avd->vlan = ntohs(*(unsigned short*)r); + avc->vlan = ntohs(*(unsigned short*)r); r += 2; for (port = 0; port < ports; port++) { /* FIXME: if ports > 8 */ if ((r[1] >> (7 - port)) & 1) - avd->ports[port] = VLAN_TAGGED; + avc->ports[port] = VLAN_TAGGED; else if ((r[0] >> (7 - port)) & 1) - avd->ports[port] = VLAN_UNTAGGED; + avc->ports[port] = VLAN_UNTAGGED; else - avd->ports[port] = VLAN_NO; + avc->ports[port] = VLAN_NO; } free(at->data); - at->data = avd; - at->size = sizeof(struct attr_vlan_dot) + ports; + at->data = avc; + at->size = sizeof(struct attr_vlan_conf) + ports; return true; @@ -348,18 +430,18 @@ static bool vlan_dot_decode (struct attr *at, unsigned char ports) static bool vlan_dot_encode (struct attr *at, unsigned char ports) { - struct attr_vlan_dot *avd = at->data; + struct attr_vlan_conf *avc = at->data; char *r, fl; unsigned int size, port; - if (avd->vlan < VLAN_MIN || avd->vlan > VLAN_MAX) + if (avc->vlan < VLAN_MIN || avc->vlan > VLAN_DOT_MAX) return false; /* just a header is valid */ - if (at->size == sizeof(struct attr_vlan_dot)) + if (at->size == sizeof(struct attr_vlan_conf)) size = 2; - else if (at->size == sizeof(struct attr_vlan_dot) + ports) + else if (at->size == sizeof(struct attr_vlan_conf) + ports) size = (2 + 2 * (1 + ((ports - 1) >> 3))); else return false; @@ -369,7 +451,7 @@ static bool vlan_dot_encode (struct attr *at, unsigned char ports) return false; memset(r, 0, size); - *(unsigned short*)r = htons(avd->vlan); + *(unsigned short*)r = htons(avc->vlan); if (size == 2) goto end; @@ -379,9 +461,11 @@ static bool vlan_dot_encode (struct attr *at, unsigned char ports) for (port = 0; port < ports; port++) { /* FIXME: if ports > 8 */ fl = (1 << (7 - port)); - switch (avd->ports[port]) { + switch (avc->ports[port]) { case VLAN_TAGGED: r[1] |= fl; + /* a tagged VLAN is also marked as untagged + * so do not put a "break" here */ case VLAN_UNTAGGED: r[0] |= fl; } @@ -416,6 +500,7 @@ static const struct attr_handler attrtab[] = { 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_PORT_CONF, 0, vlan_port_encode, vlan_port_decode), 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), -- 2.39.5