]> git.sur5r.net Git - ngadmin/blobdiff - lib/src/ngadmin.c
Added chained makefiles.
[ngadmin] / lib / src / ngadmin.c
index 179302e20bcaef753482dce827323677369948f8..2cbb97e640b93f3ad9d441a8dc1d092c11126096 100644 (file)
@@ -4,7 +4,7 @@
 
 
 
-static const struct timeval default_timeout={.tv_sec=3, .tv_usec=0};
+static const struct timeval default_timeout={.tv_sec=4, .tv_usec=0};
 
 
 
@@ -176,6 +176,7 @@ int ngadmin_scan (struct ngadmin *nga) {
  }
  
  free(nga->swi_tab);
+ nga->swi_tab=NULL;
  nga->swi_count=0;
  nga->current=NULL;
  
@@ -189,7 +190,7 @@ int ngadmin_scan (struct ngadmin *nga) {
  
  // send request to all potential switches
  i=sendNgPacket(nga, CODE_READ_REQ, attr);
destroyList(attr, (void(*)(void*))freeAttr);
clearList(attr, (void(*)(void*))freeAttr);
  if ( i<0 ) {
   return ERR_NET;
  }
@@ -197,10 +198,10 @@ int ngadmin_scan (struct ngadmin *nga) {
  
  // try to receive any packets until timeout
  swiList=createEmptyList();
- while ( (attr=recvNgPacket(nga, CODE_READ_REP, NULL, NULL))!=NULL ) {
+ while ( recvNgPacket(nga, CODE_READ_REP, NULL, NULL, attr)>=0 ) {
   sa=malloc(sizeof(struct swi_attr));
   extractSwitchAttributes(sa, attr);
-  destroyList(attr, (void(*)(void*))freeAttr);
+  clearList(attr, (void(*)(void*))freeAttr);
   pushBackList(swiList, sa);
  }
  
@@ -251,10 +252,8 @@ const struct swi_attr* ngadmin_getCurrentSwitch (struct ngadmin *nga) {
 int ngadmin_login (struct ngadmin *nga, int id) {
  
  List *attr;
- int ret=ERR_OK, i;
+ int ret=ERR_OK;
  struct swi_attr *sa;
- char err;
- unsigned short attr_error;
  
  
  if ( nga==NULL ) {
@@ -269,27 +268,17 @@ int ngadmin_login (struct ngadmin *nga, int id) {
  
  attr=createEmptyList();
  pushBackList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_READ_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
if ( i<0 || (attr=recvNgPacket(nga, CODE_READ_REP, &err, &attr_error))==NULL ) {
-  ret=ERR_NET;
+ if ( (ret=readRequest(nga, attr))==ERR_OK ) {
+  // login succeeded
+  // TODO: if keep broadcasting is disabled, connect() the UDP socket so icmp errors messages (port unreachable, TTL exceeded in transit, ...)can be received
} else {
+  // login failed
   nga->current=NULL;
-  goto end;
  }
  
  destroyList(attr, (void(*)(void*))freeAttr);
- if ( err==7 && attr_error==ATTR_PASSWORD ) {
-  ret=ERR_BADPASS;
-  nga->current=NULL;
-  goto end;
- }
  
  
- end:
  return ret;
  
 }
@@ -315,11 +304,7 @@ int ngadmin_getPortsStatus (struct ngadmin *nga, unsigned char *ports) {
  
  attr=createEmptyList();
  pushBackList(attr, newEmptyAttr(ATTR_PORT_STATUS));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_READ_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_READ_REP, NULL, NULL))==NULL ) {
-  ret=ERR_NET;
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
@@ -331,10 +316,9 @@ int ngadmin_getPortsStatus (struct ngadmin *nga, unsigned char *ports) {
   }
  }
  
- destroyList(attr, (void(*)(void*))freeAttr);
  
  end:
+ destroyList(attr, (void(*)(void*))freeAttr);
  
  return ret;
  
@@ -346,9 +330,7 @@ int ngadmin_getPortsStatus (struct ngadmin *nga, unsigned char *ports) {
 int ngadmin_setName (struct ngadmin *nga, const char *name) {
  
  List *attr;
- int ret=ERR_OK, i;
- char err;
- unsigned short attr_error;
+ int ret=ERR_OK;
  
  
  if ( nga==NULL ) {
@@ -359,23 +341,8 @@ int ngadmin_setName (struct ngadmin *nga, const char *name) {
  
  
  attr=createEmptyList();
- pushBackList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
- if ( name==NULL ) {
-  pushBackList(attr, newEmptyAttr(ATTR_NAME));
- } else {
-  pushBackList(attr, newAttr(ATTR_NAME, strlen(name), strdup(name)));
- }
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_WRITE_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error))==NULL ) {
-  ret=ERR_NET;
-  goto end;
- }
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( err==7 && attr_error==ATTR_PASSWORD ) {
-  ret=ERR_BADPASS;
+ pushBackList(attr, name==NULL ? newEmptyAttr(ATTR_NAME) : newAttr(ATTR_NAME, strlen(name), strdup(name)) );
+ if ( (ret=writeRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
@@ -402,7 +369,7 @@ int ngadmin_getPortsStatistics (struct ngadmin *nga, struct port_stats *ps) {
  List *attr;
  ListNode *ln;
  struct attr *at;
- int ret=ERR_OK, i;
+ int ret=ERR_OK;
  int port;
  
  
@@ -415,14 +382,11 @@ int ngadmin_getPortsStatistics (struct ngadmin *nga, struct port_stats *ps) {
  
  attr=createEmptyList();
  pushBackList(attr, newEmptyAttr(ATTR_PORT_STATISTICS));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_READ_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_READ_REP, NULL, NULL))==NULL ) {
-  ret=ERR_NET;
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
  for (ln=attr->first; ln!=NULL; ln=ln->next) {
   at=ln->data;
   if ( at->attr==ATTR_PORT_STATISTICS && at->size>=49 && (port=(int)(*(char*)at->data)-1)>=0 && port<nga->current->ports ) {
@@ -433,10 +397,9 @@ int ngadmin_getPortsStatistics (struct ngadmin *nga, struct port_stats *ps) {
   }
  }
  
- destroyList(attr, (void(*)(void*))freeAttr);
  
  end:
+ destroyList(attr, (void(*)(void*))freeAttr);
  
  return ret;
  
@@ -448,41 +411,13 @@ int ngadmin_getPortsStatistics (struct ngadmin *nga, struct port_stats *ps) {
 int ngadmin_resetPortsStatistics (struct ngadmin *nga) {
  
  List *attr;
- int ret=ERR_OK, i;
- char err;
- unsigned short attr_error;
- if ( nga==NULL ) {
-  return ERR_INVARG;
- } else if ( nga->current==NULL ) {
-  return ERR_NOTLOG;
- }
  
  
  attr=createEmptyList();
- pushBackList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
  pushBackList(attr, newByteAttr(ATTR_STATS_RESET, 1));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_WRITE_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error))==NULL ) {
-  ret=ERR_NET;
-  goto end;
- }
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( err==7 && attr_error==ATTR_PASSWORD ) {
-  ret=ERR_BADPASS;
-  goto end;
- }
  
  
- end:
- return ret;
+ return writeRequest(nga, attr);
  
 }
 
@@ -492,9 +427,7 @@ int ngadmin_resetPortsStatistics (struct ngadmin *nga) {
 int ngadmin_changePassword (struct ngadmin *nga, const char* pass) {
  
  List *attr;
- int ret=ERR_OK, i;
- char err;
- unsigned short attr_error;
+ int ret=ERR_OK;
  
  
  if ( nga==NULL || pass==NULL ) {
@@ -505,20 +438,8 @@ int ngadmin_changePassword (struct ngadmin *nga, const char* pass) {
  
  
  attr=createEmptyList();
- pushBackList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
  pushBackList(attr, newAttr(ATTR_NEW_PASSWORD, strlen(pass), strdup(pass)));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_WRITE_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error))==NULL ) {
-  ret=ERR_NET;
-  goto end;
- }
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( err==7 && attr_error==ATTR_PASSWORD ) {
-  ret=ERR_BADPASS;
+ if ( (ret=writeRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
@@ -541,7 +462,7 @@ int ngadmin_getStormFilterState (struct ngadmin *nga, int *s) {
  List *attr;
  ListNode *ln;
  struct attr *at;
- int ret=ERR_OK, i;
+ int ret=ERR_OK;
  
  
  if ( nga==NULL || s==NULL ) {
@@ -553,14 +474,11 @@ int ngadmin_getStormFilterState (struct ngadmin *nga, int *s) {
  
  attr=createEmptyList();
  pushBackList(attr, newEmptyAttr(ATTR_STORM_ENABLE));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_READ_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_READ_REP, NULL, NULL))==NULL ) {
-  ret=ERR_NET;
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
  for (ln=attr->first; ln!=NULL; ln=ln->next) {
   at=ln->data;
   if ( at->attr==ATTR_STORM_ENABLE && at->size>=1 ) {
@@ -569,10 +487,9 @@ int ngadmin_getStormFilterState (struct ngadmin *nga, int *s) {
   }
  }
  
- destroyList(attr, (void(*)(void*))freeAttr);
  
  end:
+ destroyList(attr, (void(*)(void*))freeAttr);
  
  return ret;
  
@@ -584,40 +501,13 @@ int ngadmin_getStormFilterState (struct ngadmin *nga, int *s) {
 int ngadmin_setStormFilterState (struct ngadmin *nga, int s) {
  
  List *attr;
- int ret=ERR_OK, i;
- char err;
- unsigned short attr_error;
- if ( nga==NULL ) {
-  return ERR_INVARG;
- } else if ( nga->current==NULL ) {
-  return ERR_NOTLOG;
- }
  
  
  attr=createEmptyList();
- pushBackList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
  pushBackList(attr, newByteAttr(ATTR_STORM_ENABLE, s!=0));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_WRITE_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error))==NULL ) {
-  ret=ERR_NET;
-  goto end;
- }
  
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( err==7 && attr_error==ATTR_PASSWORD ) {
-  ret=ERR_BADPASS;
-  goto end;
- }
  
- end:
- return ret;
+ return writeRequest(nga, attr);
  
 }
 
@@ -641,25 +531,21 @@ int ngadmin_getStormFilterValues (struct ngadmin *nga, int *ports) {
  
  attr=createEmptyList();
  pushBackList(attr, newEmptyAttr(ATTR_STORM_BITRATE));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_READ_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_READ_REP, NULL, NULL))==NULL ) {
-  ret=ERR_NET;
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
  for (ln=attr->first; ln!=NULL; ln=ln->next) {
   at=ln->data;
-  if ( at->attr==ATTR_STORM_BITRATE && at->size>=5 && (i=*(char*)(at->data)-1)>=0 && i<nga->current->ports ) {
+  if ( at->attr==ATTR_STORM_BITRATE && at->size>=5 && (i=(int)*(char*)(at->data)-1)>=0 && i<nga->current->ports ) {
    ports[i]=ntohl(*(int*)(1+(char*)at->data));
   }
  }
  
- destroyList(attr, (void(*)(void*))freeAttr);
  
  end:
+ destroyList(attr, (void(*)(void*))freeAttr);
  
  return ret;
  
@@ -671,9 +557,7 @@ int ngadmin_getStormFilterValues (struct ngadmin *nga, int *ports) {
 int ngadmin_setStormFilterValues (struct ngadmin *nga, const int *ports) {
  
  List *attr;
- int ret=ERR_OK, i;
- char err;
- unsigned short attr_error;
+ int i;
  char *p;
  
  
@@ -685,7 +569,6 @@ int ngadmin_setStormFilterValues (struct ngadmin *nga, const int *ports) {
  
  
  attr=createEmptyList();
- pushBackList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
  
  for (i=0; i<nga->current->ports; ++i) {
   if ( ports[i]>=0 && ports[i]<=11 ) {
@@ -696,25 +579,8 @@ int ngadmin_setStormFilterValues (struct ngadmin *nga, const int *ports) {
   }
  }
  
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_WRITE_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error))==NULL ) {
-  ret=ERR_NET;
-  goto end;
- }
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( err==7 && attr_error==ATTR_PASSWORD ) {
-  ret=ERR_BADPASS;
-  goto end;
- }
  
- end:
- return ret;
+ return writeRequest(nga, attr);
  
 }
 
@@ -726,7 +592,11 @@ int ngadmin_getBitrateLimits (struct ngadmin *nga, int *ports) {
  List *attr;
  ListNode *ln;
  struct attr *at;
- int ret=ERR_OK, i;
+ int ret=ERR_OK;
+ struct {
+  char port;
+  int bitrate;
+ } __attribute__((packed)) *p;
  
  
  if ( nga==NULL || ports==NULL ) {
@@ -739,27 +609,25 @@ int ngadmin_getBitrateLimits (struct ngadmin *nga, int *ports) {
  attr=createEmptyList();
  pushBackList(attr, newEmptyAttr(ATTR_BITRATE_INPUT));
  pushBackList(attr, newEmptyAttr(ATTR_BITRATE_OUTPUT));
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_READ_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_READ_REP, NULL, NULL))==NULL ) {
-  ret=ERR_NET;
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
  for (ln=attr->first; ln!=NULL; ln=ln->next) {
   at=ln->data;
-  if ( at->attr==ATTR_BITRATE_INPUT && at->size>=5 && (i=*(char*)(at->data)-1)>=0 && i<nga->current->ports ) {
-   ports[i*2+0]=ntohl(*(int*)(1+(char*)at->data));
-  } else if ( at->attr==ATTR_BITRATE_OUTPUT && at->size>=5 && (i=*(char*)(at->data)-1)>=0 && i<nga->current->ports ) {
-   ports[i*2+1]=ntohl(*(int*)(1+(char*)at->data));
+  p=at->data;
+  if ( at->size<sizeof(*p) || p->port<1 || p->port>nga->current->ports ) continue;
+  if ( at->attr==ATTR_BITRATE_INPUT ) {
+   ports[(p->port-1)*2+0]=ntohl(p->bitrate);
+  } else if ( at->attr==ATTR_BITRATE_OUTPUT ) {
+   ports[(p->port-1)*2+1]=ntohl(p->bitrate);
   }
  }
  
- destroyList(attr, (void(*)(void*))freeAttr);
  
  end:
+ destroyList(attr, (void(*)(void*))freeAttr);
  
  return ret;
  
@@ -771,9 +639,7 @@ int ngadmin_getBitrateLimits (struct ngadmin *nga, int *ports) {
 int ngadmin_setBitrateLimits (struct ngadmin *nga, const int *ports) {
  
  List *attr;
- int ret=ERR_OK, i;
- char err;
- unsigned short attr_error;
+ int i;
  char *p;
  
  
@@ -785,7 +651,6 @@ int ngadmin_setBitrateLimits (struct ngadmin *nga, const int *ports) {
  
  
  attr=createEmptyList();
- pushBackList(attr, newAttr(ATTR_PASSWORD, strlen(nga->password), strdup(nga->password)));
  
  for (i=0; i<nga->current->ports; ++i) {
   if ( ports[2*i+0]>=0 && ports[2*i+0]<=11 ) {
@@ -802,26 +667,863 @@ int ngadmin_setBitrateLimits (struct ngadmin *nga, const int *ports) {
   }
  }
  
- pushBackList(attr, newEmptyAttr(ATTR_END));
- i=sendNgPacket(nga, CODE_WRITE_REQ, attr);
- destroyList(attr, (void(*)(void*))freeAttr);
- if ( i<0 || (attr=recvNgPacket(nga, CODE_WRITE_REP, &err, &attr_error))==NULL ) {
-  ret=ERR_NET;
+ return writeRequest(nga, attr);
+}
+
+
+
+// -------------------------------------------------
+int ngadmin_getQOSMode (struct ngadmin *nga, int *s) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ int ret=ERR_OK;
+ if ( nga==NULL || s==NULL ) {
+  return ERR_INVARG;
+ } else if ( nga->current==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newEmptyAttr(ATTR_QOS_TYPE));
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
   goto end;
  }
  
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  if ( at->attr==ATTR_QOS_TYPE && at->size>=1 ) {
+   *s= *(char*)at->data ;
+   break;
+  }
+ }
+ end:
  destroyList(attr, (void(*)(void*))freeAttr);
  
- if ( err==7 && attr_error==ATTR_PASSWORD ) {
-  ret=ERR_BADPASS;
+ return ret;
+}
+
+
+// ------------------------------------------------
+int ngadmin_setQOSMode (struct ngadmin *nga, int s) {
+ List *attr;
+ if ( s<QOS_PORT || s>QOS_DOT ) {
+  return ERR_INVARG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newByteAttr(ATTR_QOS_TYPE, s));
+ return writeRequest(nga, attr);
+}
+
+
+
+// -------------------------------------------------------
+int ngadmin_getQOSValues (struct ngadmin *nga, char *ports) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ int ret=ERR_OK;
+ char *p;
+ if ( nga==NULL || ports==NULL ) {
+  return ERR_INVARG;
+ } else if ( nga->current==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newEmptyAttr(ATTR_QOS_CONFIG));
+ if ( (ret=readRequest(nga, attr))<0 ) {
   goto end;
  }
  
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  p=at->data;
+  if ( at->attr==ATTR_QOS_CONFIG && at->size>=2 && --p[0]>=0 && p[0]<nga->current->ports ) {
+   ports[(int)p[0]]=p[1];
+  }
+ }
  
  end:
+ destroyList(attr, (void(*)(void*))freeAttr);
  
  return ret;
  
 }
 
 
+
+// --------------------------------------------------------------
+int ngadmin_setQOSValues (struct ngadmin *nga, const char *ports) {
+ List *attr;
+ int i;
+ char *p;
+ if ( nga==NULL || ports==NULL ) {
+  return ERR_INVARG;
+ } else if ( nga->current==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ for (i=0; i<nga->current->ports; ++i) {
+  if ( ports[i]>=PRIO_HIGH && ports[i]<=PRIO_LOW ) {
+   p=malloc(2);
+   p[0]=i+1;
+   p[1]=ports[i];
+   pushBackList(attr, newAttr(ATTR_QOS_CONFIG, 2, p));
+  }
+ }
+ return writeRequest(nga, attr);
+}
+
+
+
+// --------------------------------------
+int ngadmin_restart (struct ngadmin *nga) {
+ List *attr;
+ attr=createEmptyList();
+ pushBackList(attr, newByteAttr(ATTR_RESTART, 1));
+ return writeRequest(nga, attr);
+}
+
+
+
+// ---------------------------------------
+int ngadmin_defaults (struct ngadmin *nga) {
+ List *attr;
+ int ret=ERR_OK;
+ attr=createEmptyList();
+ pushBackList(attr, newByteAttr(ATTR_DEFAULTS, 1));
+ if ( (ret=writeRequest(nga, attr))!=ERR_OK ) {
+  goto end;
+ }
+ // successful: delog and clean list
+ free(nga->swi_tab);
+ nga->swi_tab=NULL;
+ nga->swi_count=0;
+ nga->current=NULL;
+ end:
+ return ret;
+}
+
+
+
+// -----------------------------------------------------
+int ngadmin_getMirror (struct ngadmin *nga, char *ports) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ struct swi_attr *sa;
+ int ret=ERR_OK, i;
+ unsigned char *p;
+ if ( nga==NULL || ports==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newEmptyAttr(ATTR_MIRROR));
+ if ( (ret=readRequest(nga, attr))<0 ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  p=at->data;
+  if ( at->attr==ATTR_MIRROR && at->size>=3 && p[0]<=nga->current->ports ) {
+   ports[0]=p[0];
+   for (i=1; i<=sa->ports; ++i) { // FIXME: if ports>8
+    ports[i]=(p[2]>>(8-i))&1;
+   }
+   break;
+  }
+ }
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// -----------------------------------------------------------
+int ngadmin_setMirror (struct ngadmin *nga, const char *ports) {
+ List *attr;
+ int i;
+ char *p;
+ struct swi_attr *sa;
+ if ( nga==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ p=malloc(3); // FIXME: if ports>8
+ memset(p, 0, 3);
+ if ( ports!=NULL && ports[0]>0 && ports[0]<=sa->ports ) {
+  p[0]=ports[0];
+  for (i=1; i<=sa->ports; ++i) {
+   if ( i!=p[0] ) {
+    p[2]|=(ports[i]&1)<<(8-i);
+   }
+  }
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newAttr(ATTR_MIRROR, 3, p));
+ return writeRequest(nga, attr);
+}
+
+
+
+// ----------------------------------------------------------------
+int ngadmin_getIGMPConf (struct ngadmin *nga, struct igmp_conf *ic) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ struct swi_attr *sa;
+ int ret=ERR_OK;
+ unsigned char *p;
+ unsigned short *s;
+ if ( nga==NULL || ic==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ /*
+ ATTR_IGMP_ENABLE_VLAN
+ ATTR_IGMP_BLOCK_UNK
+ ATTR_IGMP_VALID_V3
+ Apparently, read-querying these attributes at the same time causes the switch to reply garbage. 
+ Here we are forced to do like the official win app and send a separate request for each attribute. 
+ */
+ attr=createEmptyList();
+ memset(ic, 0, sizeof(struct igmp_conf));
+ pushBackList(attr, newEmptyAttr(ATTR_IGMP_ENABLE_VLAN));
+ if ( (ret=readRequest(nga, attr))<0 ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  s=at->data;
+  if ( at->attr==ATTR_IGMP_ENABLE_VLAN && at->size>=4 ) {
+   ic->enable= ntohs(s[0])!=0 ;
+   ic->vlan=htons(s[1]);
+   break;
+  }
+ }
+ clearList(attr, (void(*)(void*))freeAttr);
+ pushBackList(attr, newEmptyAttr(ATTR_IGMP_BLOCK_UNK));
+ if ( (ret=readRequest(nga, attr))<0 ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  p=at->data;
+  if ( at->attr==ATTR_IGMP_BLOCK_UNK && at->size>=1 ) {
+   ic->block= p[0]!=0 ;
+   break;
+  }
+ }
+ clearList(attr, (void(*)(void*))freeAttr);
+ pushBackList(attr, newEmptyAttr(ATTR_IGMP_VALID_V3));
+ if ( (ret=readRequest(nga, attr))<0 ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  p=at->data;
+  if ( at->attr==ATTR_IGMP_VALID_V3 && at->size>=1 ) {
+   ic->validate= p[0]!=0 ;
+   break;
+  }
+ }
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// ----------------------------------------------------------------------
+int ngadmin_setIGMPConf (struct ngadmin *nga, const struct igmp_conf *ic) {
+ List *attr;
+ short *s;
+ struct swi_attr *sa;
+ if ( nga==NULL || ic==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ s=malloc(2*sizeof(short));
+ s[0]=htons(ic->enable!=false);
+ s[1]=htons(ic->vlan&0x0FFF);
+ attr=createEmptyList();
+ pushBackList(attr, newAttr(ATTR_IGMP_ENABLE_VLAN, 2*sizeof(short), s));
+ pushBackList(attr, newByteAttr(ATTR_IGMP_BLOCK_UNK, ic->block!=false ));
+ pushBackList(attr, newByteAttr(ATTR_IGMP_VALID_V3, ic->validate!=false ));
+ return writeRequest(nga, attr);
+}
+
+
+
+// ----------------------------------------------------------------------
+int ngadmin_cabletest (struct ngadmin *nga, struct cabletest *ct, int nb) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ int i, ret=ERR_OK;
+ struct swi_attr *sa;
+ char *p;
+ if ( nga==NULL || ct==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ for (i=0; i<nb; ++i) {
+  if ( ct[i].port>=1 && ct[i].port<=sa->ports ) {
+   
+   p=malloc(2);
+   p[0]=ct[i].port;
+   p[1]=1;
+   pushBackList(attr, newAttr(ATTR_CABLETEST_DO, 2, p));
+   
+   ret=writeRequest(nga, attr);
+   attr=NULL;
+   if ( ret<0 ) goto end;
+   
+   // the list is destroyed by writeRequest, so we need to recreate it
+   attr=createEmptyList();
+   pushBackList(attr, newByteAttr(ATTR_CABLETEST_RESULT, ct[i].port));
+   
+   if ( (ret=readRequest(nga, attr))<0 ) goto end;
+   
+   for (ln=attr->first; ln!=NULL; ln=ln->next) {
+    at=ln->data;
+    p=at->data;
+    if ( at->attr==ATTR_CABLETEST_RESULT && at->size>=9 && p[0]==ct[i].port ) {
+     ct[i].v1=ntohl(*(int*)&p[1]);
+     ct[i].v2=ntohl(*(int*)&p[5]);
+     break;
+    }
+   }
+   
+   // just empty the list, it will be used at next iteration
+   clearList(attr, (void(*)(void*))freeAttr);
+   
+  }
+ }
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// --------------------------------------------------------------------
+int ngadmin_setNetConf (struct ngadmin *nga, const struct net_conf *nc) {
+ List *attr;
+ struct swi_attr *sa;
+ int ret=ERR_OK;
+ if ( nga==NULL || nc==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ if ( nc->dhcp ) {
+  pushBackList(attr, newByteAttr(ATTR_DHCP, 1));
+ } else {
+  pushBackList(attr, newByteAttr(ATTR_DHCP, 0));
+  // only add non-null values
+  if ( nc->ip.s_addr!=0 ) pushBackList(attr, newAddrAttr(ATTR_IP, nc->ip));
+  if ( nc->netmask.s_addr!=0 ) pushBackList(attr, newAddrAttr(ATTR_NETMASK, nc->netmask));
+  if ( nc->gw.s_addr!=0 ) pushBackList(attr, newAddrAttr(ATTR_GATEWAY, nc->gw));
+ }
+ if ( (ret=writeRequest(nga, attr))!=ERR_OK ) {
+  goto end;
+ }
+ // update local values
+ sa->nc.dhcp=nc->dhcp;
+ if ( !nc->dhcp ) {
+  if ( nc->ip.s_addr!=0 ) sa->nc.ip=nc->ip;
+  if ( nc->netmask.s_addr!=0 ) sa->nc.netmask=nc->netmask;
+  if ( nc->gw.s_addr!=0 ) sa->nc.gw=nc->gw;
+ }
+ end:
+ return ret;
+}
+
+
+
+// --------------------------------------------------
+int ngadmin_getVLANType (struct ngadmin *nga, int *t) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ int ret=ERR_OK;
+ if ( nga==NULL || t==NULL ) {
+  return ERR_INVARG;
+ } else if ( nga->current==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newEmptyAttr(ATTR_VLAN_TYPE));
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  if ( at->attr==ATTR_VLAN_TYPE && at->size>=1 ) {
+   *t= (int)*(char*)at->data ;
+   break;
+  }
+ }
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// -------------------------------------------------
+int ngadmin_setVLANType (struct ngadmin *nga, int t) {
+ List *attr;
+ struct swi_attr *sa;
+ if ( nga==NULL || t<1 || t>4 ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newByteAttr(ATTR_VLAN_TYPE, t));
+ return writeRequest(nga, attr);
+}
+
+
+
+// ------------------------------------------------------------------------------------------------------
+int ngadmin_getVLANDotAllConf (struct ngadmin *nga, unsigned short *vlans, unsigned char *ports, int *nb) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ struct swi_attr *sa;
+ int ret=ERR_OK, total, i;
+ char *p=NULL;
+ if ( nga==NULL || vlans==NULL || ports==NULL || nb==NULL || *nb<=0 ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ total=*nb;
+ *nb=0;
+ attr=createEmptyList();
+ pushBackList(attr, newEmptyAttr(ATTR_VLAN_DOT_CONF));
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  p=at->data;
+  if ( *nb>=total ) break; // no more room
+  if ( at->attr==ATTR_VLAN_DOT_CONF && at->size>=4 ) {
+   for (i=0; i<sa->ports; ++i) {
+    if ( (p[3]>>(7-i))&1 ) ports[i]=VLAN_TAGGED; // tagged
+    else if ( (p[2]>>(7-i))&1 ) ports[i]=VLAN_UNTAGGED; // untagged
+    else ports[i]=VLAN_NO;
+   }
+   *vlans++=ntohs(*(unsigned short*)p);
+   ports+=sa->ports;
+   ++*nb;
+  }
+ }
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// ----------------------------------------------------------------------------------------
+int ngadmin_getVLANDotConf (struct ngadmin *nga, unsigned short vlan, unsigned char *ports) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ struct swi_attr *sa;
+ int ret=ERR_OK, i;
+ char *p=NULL;
+ if ( nga==NULL || vlan<1 || vlan>VLAN_MAX || ports==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newShortAttr(ATTR_VLAN_DOT_CONF, vlan));
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  p=at->data;
+  if ( at->attr==ATTR_VLAN_DOT_CONF && at->size>=4 ) {
+   for (i=0; i<sa->ports; ++i) {
+    if ( (p[3]>>(7-i))&1 ) ports[i]=VLAN_TAGGED; // tagged
+    else if ( (p[2]>>(7-i))&1 ) ports[i]=VLAN_UNTAGGED; // untagged
+    else ports[i]=VLAN_NO;
+   }
+   break;
+  }
+ }
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// ----------------------------------------------------------------------------------------------
+int ngadmin_setVLANDotConf (struct ngadmin *nga, unsigned short vlan, const unsigned char *ports) {
+ List *attr=NULL;
+ ListNode *ln;
+ struct attr *at;
+ struct swi_attr *sa;
+ char *p, fl;
+ int ret=ERR_OK, i;
+ if ( nga==NULL || vlan<1 || vlan>VLAN_MAX || ports==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ // if nothing is to be changed, do nothing
+ for (i=0; i<sa->ports && ports[i]==VLAN_UNSPEC; ++i);
+ if ( i==sa->ports ) goto end;
+ attr=createEmptyList();
+ p=malloc(4);
+ *(unsigned short*)p=htons(vlan);
+ *(unsigned short*)&p[2]=0;
+ // if all is to be changed, we do not need to read old config
+ if ( memchr(ports, VLAN_UNSPEC, sa->ports)!=NULL ) {
+  
+  attr=createEmptyList();
+  pushBackList(attr, newShortAttr(ATTR_VLAN_DOT_CONF, vlan));
+  if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
+   goto end;
+  }
+  
+  for (ln=attr->first; ln!=NULL; ln=ln->next) {
+   at=ln->data;
+   if ( at->attr==ATTR_VLAN_DOT_CONF && at->size>=4 ) {
+    *(unsigned short*)&p[2]=*(unsigned short*)(at->data+2);
+    break;
+   }
+  }
+  
+  clearList(attr, (void(*)(void*))freeAttr);
+  
+ }
+ // apply changes
+ for (i=0; i<sa->ports; ++i) {
+  fl=(1<<(7-i));
+  switch ( ports[i] ) {
+   case VLAN_NO:
+    p[2]&=~fl;
+    p[3]&=~fl;
+   break;
+   case VLAN_UNTAGGED:
+    p[2]|=fl;
+    p[3]&=~fl;
+   break;
+   case VLAN_TAGGED:
+    p[2]|=fl;
+    p[3]|=fl;
+  }
+ }
+ pushBackList(attr, newAttr(ATTR_VLAN_DOT_CONF, 4, p));
+ ret=writeRequest(nga, attr);
+ attr=NULL;
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// ---------------------------------------------------------------
+int ngadmin_VLANDestroy (struct ngadmin *nga, unsigned short vlan) {
+ List *attr;
+ struct swi_attr *sa;
+ if ( nga==NULL || vlan<1 || vlan>VLAN_MAX ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newShortAttr(ATTR_VLAN_DESTROY, vlan));
+ return writeRequest(nga, attr);
+}
+
+
+
+// ----------------------------------------------------------------
+int ngadmin_getAllPVID (struct ngadmin *nga, unsigned short *ports) {
+ List *attr;
+ ListNode *ln;
+ struct attr *at;
+ struct swi_attr *sa;
+ int ret=ERR_OK;
+ char *p;
+ if ( nga==NULL || ports==NULL ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ }
+ attr=createEmptyList();
+ pushBackList(attr, newEmptyAttr(ATTR_VLAN_PVID));
+ if ( (ret=readRequest(nga, attr))!=ERR_OK ) {
+  goto end;
+ }
+ for (ln=attr->first; ln!=NULL; ln=ln->next) {
+  at=ln->data;
+  p=at->data;
+  if ( at->attr==ATTR_VLAN_PVID && at->size>=3 && p[0]>=1 && p[0]<=sa->ports ) {
+   ports[p[0]-1]=htons(*(unsigned short*)&p[1]);
+  }
+ }
+ end:
+ destroyList(attr, (void(*)(void*))freeAttr);
+ return ret;
+}
+
+
+
+// -------------------------------------------------------------------------------
+int ngadmin_setPVID (struct ngadmin *nga, unsigned char port, unsigned short vlan) {
+ List *attr;
+ struct swi_attr *sa;
+ char *p;
+ if ( nga==NULL || port<1 || vlan<1 || vlan>VLAN_MAX ) {
+  return ERR_INVARG;
+ } else if ( (sa=nga->current)==NULL ) {
+  return ERR_NOTLOG;
+ } else if ( port>sa->ports ) {
+  return ERR_INVARG;
+ }
+ attr=createEmptyList();
+ p=malloc(3);
+ p[0]=port;
+ *(unsigned short*)&p[1]=htons(vlan);
+ pushBackList(attr, newAttr(ATTR_VLAN_PVID, 3, p));
+ return writeRequest(nga, attr);;
+}
+
+
+