www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / userapps / opensource / udhcp / options.c
1 /* 
2  * options.c -- DHCP server option packet tools 
3  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
4  */
5  
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "debug.h"
11 #include "dhcpd.h"
12 #include "files.h"
13 #include "options.h"
14 #include "leases.h"
15
16
17 /* supported options are easily added here */
18 struct dhcp_option options[] = {
19         /* name[10]     flags                           code */
20         {"subnet",      OPTION_IP,                      0x01},
21         {"timezone",    OPTION_S32,                     0x02},
22         {"router",      OPTION_IP | OPTION_LIST,        0x03},
23         {"timesvr",     OPTION_IP | OPTION_LIST,        0x04},
24         {"namesvr",     OPTION_IP | OPTION_LIST,        0x05},
25         {"dns",         OPTION_IP | OPTION_LIST,        0x06},
26         {"logsvr",      OPTION_IP | OPTION_LIST,        0x07},
27         {"cookiesvr",   OPTION_IP | OPTION_LIST,        0x08},
28         {"lprsvr",      OPTION_IP | OPTION_LIST,        0x09},
29         {"hostname",    OPTION_STRING,                  0x0c},
30         {"bootsize",    OPTION_U16,                     0x0d},
31         {"domain",      OPTION_STRING,                  0x0f},
32         {"swapsvr",     OPTION_IP,                      0x10},
33         {"rootpath",    OPTION_STRING,                  0x11},
34         {"ipttl",       OPTION_U8,                      0x17},
35         {"mtu",         OPTION_U16,                     0x1a},
36         {"broadcast",   OPTION_IP,                      0x1c},
37         {"ntpsrv",      OPTION_IP | OPTION_LIST,        0x2a},
38         {"wins",        OPTION_IP | OPTION_LIST,        0x2c},
39         {"requestip",   OPTION_IP,                      0x32},
40         {"lease",       OPTION_U32,                     0x33},
41         {"dhcptype",    OPTION_U8,                      0x35},
42         {"serverid",    OPTION_IP,                      0x36},
43         {"tftp",        OPTION_STRING,                  0x42},
44         {"bootfile",    OPTION_STRING,                  0x43},
45         {"",            0x00,                           0x00}
46 };
47
48 /* Lengths of the different option types */
49 int option_lengths[] = {
50         [OPTION_IP] =           4,
51         [OPTION_IP_PAIR] =      8,
52         [OPTION_BOOLEAN] =      1,
53         [OPTION_STRING] =       0,
54         [OPTION_U8] =           1,
55         [OPTION_U16] =          2,
56         [OPTION_S16] =          2,
57         [OPTION_U32] =          4,
58         [OPTION_S32] =          4
59 };
60
61
62 /* get an option with bounds checking (warning, not aligned). */
63 unsigned char *get_option(struct dhcpMessage *packet, int code)
64 {
65         int i, length;
66         static char err[] = "bogus packet, option fields too long."; /* save a few bytes */
67         unsigned char *optionptr;
68         int over = 0, done = 0, curr = OPTION_FIELD;
69         
70         optionptr = packet->options;
71         i = 0;
72         length = 308;
73         while (!done) {
74                 if (i >= length) {
75                         LOG(LOG_WARNING, err);
76                         return NULL;
77                 }
78                 if (optionptr[i + OPT_CODE] == code) {
79                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
80                                 LOG(LOG_WARNING, err);
81                                 return NULL;
82                         }
83                         return optionptr + i + 2;
84                 }                       
85                 switch (optionptr[i + OPT_CODE]) {
86                 case DHCP_PADDING:
87                         i++;
88                         break;
89                 case DHCP_OPTION_OVER:
90                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
91                                 LOG(LOG_WARNING, err);
92                                 return NULL;
93                         }
94                         over = optionptr[i + 3];
95                         i += optionptr[OPT_LEN] + 2;
96                         break;
97                 case DHCP_END:
98                         if (curr == OPTION_FIELD && over & FILE_FIELD) {
99                                 optionptr = packet->file;
100                                 i = 0;
101                                 length = 128;
102                                 curr = FILE_FIELD;
103                         } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
104                                 optionptr = packet->sname;
105                                 i = 0;
106                                 length = 64;
107                                 curr = SNAME_FIELD;
108                         } else done = 1;
109                         break;
110                 default:
111                         i += optionptr[OPT_LEN + i] + 2;
112                 }
113         }
114         return NULL;
115 }
116
117
118 /* return the position of the 'end' option (no bounds checking) */
119 int end_option(unsigned char *optionptr) 
120 {
121         int i = 0;
122         
123         while (optionptr[i] != DHCP_END) {
124                 if (optionptr[i] == DHCP_PADDING) i++;
125                 else i += optionptr[i + OPT_LEN] + 2;
126         }
127         return i;
128 }
129
130
131 /* add an option string to the options (an option string contains an option code,
132  * length, then data) */
133 int add_option_string(unsigned char *optionptr, unsigned char *string)
134 {
135         int i, end = end_option(optionptr);
136         
137         /* end position + string length + option code/length + end option */
138         if (end + string[OPT_LEN] + 2 + 1 >= 308) {
139                 for (i = 0; options[i].code && options[i].code != string[OPT_CODE]; i++);
140                 LOG(LOG_ERR, "Option %s (0x%02x) did not fit into the packet!", 
141                         options[i].code ? options[i].name : "unknown", string[OPT_CODE]);
142                 return 0;
143         }
144         DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
145         memcpy(optionptr + end, string, string[OPT_LEN] + 2);
146         optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
147         return string[OPT_LEN] + 2;
148 }
149
150
151 /* add a one to four byte option to a packet */
152 int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data)
153 {
154         char length = 0;
155         int i, end;
156         char buffer[4]; /* Cant copy straight to optionptr, it might not be aligned */
157
158         for (i = 0; options[i].code; i++)
159                 if (options[i].code == code) {
160                         length = option_lengths[options[i].flags & TYPE_MASK];
161                         break;
162                 }
163                 
164         if (!length) {
165                 DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
166                 return 0;
167         }
168         
169         DEBUG(LOG_INFO, "adding option 0x%02x", code);
170         end = end_option(optionptr);
171         optionptr[end + OPT_CODE] = code;
172         optionptr[end + OPT_LEN] = length;
173
174         switch (length) {
175                 case 1: buffer[0] = (char) data; break;
176                 case 2: *((u_int16_t *) buffer) = htons(data); break;
177                 case 4: *((u_int32_t *) buffer) = htonl(data); break;
178         }
179         memcpy(&optionptr[end + 2], buffer, length);
180         optionptr[end + length + 2] = DHCP_END;
181         return length;
182 }
183
184
185 /* find option 'code' in opt_list */
186 struct option_set *find_option(struct option_set *opt_list, char code)
187 {
188         while (opt_list && opt_list->data[OPT_CODE] < code)
189                 opt_list = opt_list->next;
190
191         if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
192         else return NULL;
193 }
194
195
196 /* add an option to the opt_list */
197 void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
198 {
199         struct option_set *existing, *new, **curr;
200
201         /* add it to an existing option */
202         if ((existing = find_option(*opt_list, option->code))) {
203                 DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
204                 if (option->flags & OPTION_LIST) {
205                         if (existing->data[OPT_LEN] + length <= 255) {
206                                 existing->data = realloc(existing->data, 
207                                                 existing->data[OPT_LEN] + length + 2);
208                                 memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
209                                 existing->data[OPT_LEN] += length;
210                         } /* else, ignore the data, we could put this in a second option in the future */
211                 } /* else, ignore the new data */
212         } else {
213                 DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
214                 
215                 /* make a new option */
216                 new = malloc(sizeof(struct option_set));
217                 new->data = malloc(length + 2);
218                 new->data[OPT_CODE] = option->code;
219                 new->data[OPT_LEN] = length;
220                 memcpy(new->data + 2, buffer, length);
221                 
222                 curr = opt_list;
223                 while (*curr && (*curr)->data[OPT_CODE] < option->code)
224                         curr = &(*curr)->next;
225                         
226                 new->next = *curr;
227                 *curr = new;            
228         }
229 }
230
231
232 //brcm begin
233 void viInfoFree(pVI_OPTION_INFO pInfo)
234 {
235   if (pInfo) {
236     free(pInfo->oui);
237     free(pInfo->serialNumber);
238     free(pInfo->productClass);
239     free(pInfo);
240   }
241 }
242
243 void addViToList(pVI_OPTION_INFO pNew) {
244   pVI_OPTION_INFO pPtr= NULL;
245
246   /* if VI exists already, don't add, just update the info */
247   if (viList->count > 0) {
248     pPtr = viList->pHead;
249     while (pPtr) {
250       if (pPtr->ipAddr == pNew->ipAddr) {
251         /* found it, just copy the info over */
252         memcpy(pPtr,pNew,sizeof(VI_OPTION_INFO));
253         viInfoFree(pNew);
254         return;
255       }
256       pPtr = pPtr->next;
257     } /* while */
258   } /* list has something */
259
260   if (viList->pHead == NULL) {
261     viList->pHead = pNew;
262     viList->pTail = pNew;
263   }
264   else {
265     viList->pTail->next = pNew;
266     viList->pTail = pNew;
267   }
268   viList->count++;
269 }
270
271 void viListFree(void)
272 {
273   pVI_OPTION_INFO pInfo;
274
275   while (viList->pHead) {
276     pInfo = viList->pHead;
277     viList->pHead = viList->pHead->next;
278     free(pInfo->oui);
279     free(pInfo->serialNumber);
280     free(pInfo->productClass);
281     free(pInfo);
282   }
283   viList->count = 0;
284 }
285
286
287 /* this function generates VI info based on TR111 part I.
288    If -1 is return, there is no VI info found.  Otherwise, 0 is returned.
289    Type specifies gateway vendor info or device vendor info.   
290    VIinfo is where the option string is stored */
291 int createVIoption(int type, char *VIinfo)
292 {
293   FILE* fs;
294   char optionData[VENDOR_IDENTIFYING_INFO_LEN], *dataPtr;
295   int len, totalLen = 0;
296   char line[VENDOR_IDENTIFYING_INFO_LEN];
297   char oui[6];
298   char serialNum[64];
299   char productClass[64];
300
301   fs = fopen("/var/fyi/sys/info", "r");
302   if (fs == NULL) {
303     return (-1);
304   }
305   fgets(line, VENDOR_IDENTIFYING_INFO_LEN, fs);
306   sscanf(line, "OUI %s\n", oui);
307   fgets(line, VENDOR_IDENTIFYING_INFO_LEN, fs);
308   sscanf(line, "SerialNumber %s\n", serialNum);
309   fgets(line, VENDOR_IDENTIFYING_INFO_LEN, fs);
310   sscanf(line, "ProductClass %s\n", productClass);
311   fclose(fs);
312
313   optionData[VENDOR_OPTION_CODE_OFFSET] = (char)VENDOR_IDENTIFYING_OPTION_CODE;
314   *(unsigned int*)(optionData+VENDOR_OPTION_ENTERPRISE_OFFSET) = (unsigned int)VENDOR_BRCM_ENTERPRISE_NUMBER;
315   dataPtr = optionData + VENDOR_OPTION_DATA_OFFSET;
316   totalLen = VENDOR_ENTERPRISE_LEN;
317   /* read system information and add it to option data */
318   /* OUI */
319   if (type == VENDOR_IDENTIFYING_FOR_DEVICE)
320     *dataPtr++ = (char)VENDOR_DEVICE_OUI_SUBCODE;
321   else 
322     *dataPtr++ = (char)VENDOR_GATEWAY_OUI_SUBCODE;
323   len = strlen(oui);
324   *dataPtr++ = len;
325   strncpy(dataPtr,oui,len);
326   dataPtr += len;
327   totalLen += (len + VENDOR_SUBCODE_AND_LEN_BYTES);
328   
329   /* Serial Number */
330   if (type == VENDOR_IDENTIFYING_FOR_DEVICE)
331     *dataPtr++ = (char)VENDOR_DEVICE_SERIAL_NUMBER_SUBCODE;
332   else
333     *dataPtr++ = (char)VENDOR_GATEWAY_SERIAL_NUMBER_SUBCODE;
334   len = strlen(serialNum);
335   *dataPtr++ = len;
336   strncpy(dataPtr,(const char*)serialNum,len);
337   dataPtr += len;
338   totalLen += (len + VENDOR_SUBCODE_AND_LEN_BYTES);
339
340   /* Product Class */
341   if (type == VENDOR_IDENTIFYING_FOR_DEVICE)
342     *dataPtr++ = VENDOR_DEVICE_PRODUCT_CLASS_SUBCODE;
343   else 
344     *dataPtr++ = VENDOR_GATEWAY_PRODUCT_CLASS_SUBCODE;
345   len = strlen(productClass);
346   *dataPtr++ = len;
347   strncpy(dataPtr,(const char*)productClass,len);
348   dataPtr += len;
349   totalLen += (len + VENDOR_SUBCODE_AND_LEN_BYTES);
350
351   optionData[VENDOR_OPTION_LEN_OFFSET] = totalLen;
352
353   /* also copy the option code and option len which is not counted in total len */
354   memcpy((void*)VIinfo,(void*)optionData,(totalLen+VENDOR_OPTION_DATA_OFFSET));
355
356   return 0;
357 }
358 /* udp_send and readIp from voice */
359 static int readIp(const char* ip)
360 {
361    int n = 0;
362    int res = 0;
363    
364    while (n < 4 && *ip)
365    {
366       if (isdigit(*ip)) 
367       {
368          res = (res << 8) | atoi(ip);
369          n++;
370          while (isdigit(*ip)) 
371          {
372             ip++;
373          }
374       } 
375       else 
376       {
377          ip++;
378                 }
379    }
380    return res;
381 }
382
383 static int notifyApp(short port, void *data, int len)
384 {
385    int sockfd;
386    struct sockaddr_in serv_addr;
387    struct sockaddr_in cli_addr;
388    
389    /* fill in server address */
390    memset(&serv_addr, 0, sizeof(serv_addr));
391    serv_addr.sin_family      = AF_INET;
392    serv_addr.sin_addr.s_addr = readIp("127.0.0.1");
393    serv_addr.sin_port        = htons(port);
394
395    /* open udp socket */
396    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
397    {
398       printf("WT-104: Could not open socket for send\n");
399       return -1; /* could not open socket */
400    }
401
402    /* bind any local address for us */ 
403    memset(&cli_addr, 0, sizeof(cli_addr));
404    cli_addr.sin_family      = AF_INET;
405    cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
406    cli_addr.sin_port        = htons(0);
407
408    if (bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) 
409    {
410       printf("dhcpd: Could not bind client socket\n");
411       return -2; /* could not bind client socket */
412    }
413  
414    /* send the data */
415    if (sendto(sockfd, data, len, 0, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) != len) 
416    {
417       printf( "dhcpd: Could not sendto\n");
418       return -3; /* could not sendto */
419    }
420    close(sockfd);
421    return 0;
422 }
423
424 /* this function parses received VI info, and save it.
425    If -1 is return, there is no VI info found; invalid option.  Otherwise, 0 is returned.
426    *option is the received option string to parse; it points to optionData.
427    ipAddr is the device's offered address or device's ip address */
428 int saveVIoption(char *option, u_int32_t ipAddr)
429 {
430   char *optionPtr;
431
432   int maxSubcode = 3;
433   int subcodeParsed = 0;
434   int subcode, sublen;
435   pVI_OPTION_INFO pInfo = NULL;
436   int ret = 0;
437   int msg = 0;
438
439   if (option == NULL) {
440     DEBUG(LOG_ERR, "saveVIoption(): option is NULL.");
441     return -1;
442   }
443
444   optionPtr = option;
445   pInfo = malloc(sizeof(VI_OPTION_INFO));
446   memset(pInfo,0,sizeof(VI_OPTION_INFO));
447   pInfo->enterprise = *(unsigned int*)(optionPtr);
448   pInfo->ipAddr = ipAddr;
449   optionPtr += VENDOR_ENTERPRISE_LEN;
450
451   while (subcodeParsed < maxSubcode) {
452      /* subcode, len, data */
453      subcode = *optionPtr++;
454      sublen = *optionPtr++;
455      subcodeParsed++;
456      switch (subcode)
457      {
458      case VENDOR_DEVICE_OUI_SUBCODE:
459      case VENDOR_GATEWAY_OUI_SUBCODE:
460         if (sublen <= VENDOR_GATEWAY_OUI_MAX_LEN) {
461            if ((pInfo->oui = malloc(sublen+1)) != NULL) {
462               memcpy(pInfo->oui,optionPtr,sublen);
463               pInfo->oui[sublen] = '\0';
464            }
465            else 
466               goto viError;
467         }
468         else {
469            DEBUG(LOG_ERR, "saveVIoption(): subcode OUI, OUI len %d is too long.",sublen);
470            goto viError;
471         }
472         break;
473      case VENDOR_DEVICE_SERIAL_NUMBER_SUBCODE:
474      case VENDOR_GATEWAY_SERIAL_NUMBER_SUBCODE:
475         if (sublen <= VENDOR_GATEWAY_SERIAL_NUMBER_MAX_LEN) {
476            if ((pInfo->serialNumber = malloc(sublen+1)) != NULL) {
477               memcpy(pInfo->serialNumber,optionPtr,sublen);
478               pInfo->serialNumber[sublen] = '\0';
479            }
480            else
481               goto viError;
482         }
483         else {
484            DEBUG(LOG_ERR, "saveVIoption(): subcode SerialNumber, Serial Number len %d is too long.",sublen);
485            goto viError;
486         }
487         break;
488      case VENDOR_DEVICE_PRODUCT_CLASS_SUBCODE:
489      case VENDOR_GATEWAY_PRODUCT_CLASS_SUBCODE:
490         if (sublen <= VENDOR_GATEWAY_PRODUCT_CLASS_MAX_LEN) {
491            if ((pInfo->productClass = malloc(sublen+1)) != NULL) {
492               memcpy(pInfo->productClass,optionPtr,sublen);
493               pInfo->productClass[sublen] = '\0';
494            }
495            else
496               goto viError;
497         }
498         else {
499            DEBUG(LOG_ERR, "saveVIoption(): subcode ProductClass, Class len %d is too long.",sublen);
500            goto viError;
501         }
502         break;
503      default:
504         DEBUG(LOG_ERR, "saveVIoption(): subcode %d, not supported.",subcode);
505         goto viError;
506      }
507      optionPtr += sublen;
508      /* add info to the manageable device link list */
509   } /* while subcodeParsed < maxSubcode */
510   addViToList(pInfo);
511   notifyApp(30006,(void*)&msg,sizeof(msg));
512
513   return ret;
514
515  viError:
516   ret = -1;
517   if (pInfo) {
518     if (pInfo->oui)
519       free(pInfo->oui);
520     if (pInfo->serialNumber)
521       free(pInfo->serialNumber);
522     if (pInfo->productClass)
523       free(pInfo->productClass);
524     free(pInfo);
525   }
526   return ret;
527 }
528
529
530 //brcm end