int ngadmin_setVLANPortConf (struct ngadmin *nga, const unsigned char *ports)
{
- List *attr = NULL;
+ List *conf_old = NULL, *conf_new = NULL;
ListNode *ln;
struct attr *at;
struct swi_attr *sa;
- struct attr_vlan_conf *avc;
+ struct attr_vlan_conf *avc_old, *avc_new;
int ret = ERR_OK, port;
- unsigned char vlan;
+ bool change;
if (nga == NULL || ports == NULL)
/* if nothing is to be changed, do nothing */
for (port = 0; port < sa->ports && ports[port] == 0; port++);
- if (port == sa->ports )
+ 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 */
+ /* read old config */
+ conf_old = createEmptyList();
+ pushBackList(conf_old, newEmptyAttr(ATTR_VLAN_PORT_CONF));
+ ret = readRequest(nga, conf_old);
+ if (ret != ERR_OK)
+ goto end;
+
+ filterAttributes(conf_old, ATTR_VLAN_PORT_CONF, ATTR_END);
+
+ /* check if the switch is in port mode */
+ if (conf_old->first == NULL) {
+ ret = ERR_INVARG;
+ goto end;
} 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));
+ at = conf_old->first->data;
+ if (at->size != sizeof(struct attr_vlan_conf) + sa->ports) {
+ ret = ERR_INVARG;
+ goto end;
}
}
- for (ln = attr->first; ln != NULL; ln = ln->next) {
+ /* merge old config with requested config */
+ conf_new = createEmptyList();
+
+ for (ln = conf_old->first; ln != NULL; ln = ln->next) {
at = ln->data;
- avc = at->data;
+ avc_old = at->data;
+
+ /* check if there is a change on this VLAN */
+ change = false;
+ for (port = 0; !change && port < sa->ports; port++) {
+ if (ports[port] == 0)
+ continue;
+ if (ports[port] == avc_old->vlan && avc_old->ports[port] == VLAN_NO)
+ change = true;
+ if (ports[port] != avc_old->vlan && avc_old->ports[port] == VLAN_UNTAGGED)
+ change = true;
+ }
+
+ /* if the VLAN is not changed, no need to send it to the switch */
+ if (!change)
+ continue;
+
+ /* compute new VLAN configuration */
+ avc_new = malloc(sizeof(struct attr_vlan_conf) + sa->ports);
+ avc_new->vlan = avc_old->vlan;
+
for (port = 0; port < sa->ports; port++) {
- if (ports[port] == avc->vlan)
- avc->ports[port] = VLAN_UNTAGGED;
+ if (ports[port] == 0)
+ avc_new->ports[port] = avc_old->ports[port];
+ else if (ports[port] == avc_new->vlan)
+ avc_new->ports[port] = VLAN_UNTAGGED;
else
- avc->ports[port] = VLAN_NO;
+ avc_new->ports[port] = VLAN_NO;
}
+
+ pushBackList(conf_new, newAttr(ATTR_VLAN_PORT_CONF, sizeof(struct attr_vlan_conf) + sa->ports, avc_new));
}
- ret = writeRequest(nga, attr);
- attr = NULL;
+ /* if no VLAN is changed, no need to send anything to the switch */
+ if (conf_new->first == NULL)
+ goto end;
+ /* send new configuration to the switch */
+ ret = writeRequest(nga, conf_new);
+ conf_new = NULL;
end:
- destroyList(attr, (void(*)(void*))freeAttr);
+ destroyList(conf_old, (void(*)(void*))freeAttr);
+ destroyList(conf_new, (void(*)(void*))freeAttr);
return ret;