2 * options.c -- DHCP server option packet tools
3 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
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},
48 /* Lengths of the different option types */
49 int option_lengths[] = {
62 /* get an option with bounds checking (warning, not aligned). */
63 unsigned char *get_option(struct dhcpMessage *packet, int code)
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;
70 optionptr = packet->options;
75 LOG(LOG_WARNING, err);
78 if (optionptr[i + OPT_CODE] == code) {
79 if (i + 1 + optionptr[i + OPT_LEN] >= length) {
80 LOG(LOG_WARNING, err);
83 return optionptr + i + 2;
85 switch (optionptr[i + OPT_CODE]) {
89 case DHCP_OPTION_OVER:
90 if (i + 1 + optionptr[i + OPT_LEN] >= length) {
91 LOG(LOG_WARNING, err);
94 over = optionptr[i + 3];
95 i += optionptr[OPT_LEN] + 2;
98 if (curr == OPTION_FIELD && over & FILE_FIELD) {
99 optionptr = packet->file;
103 } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
104 optionptr = packet->sname;
111 i += optionptr[OPT_LEN + i] + 2;
118 /* return the position of the 'end' option (no bounds checking) */
119 int end_option(unsigned char *optionptr)
123 while (optionptr[i] != DHCP_END) {
124 if (optionptr[i] == DHCP_PADDING) i++;
125 else i += optionptr[i + OPT_LEN] + 2;
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)
135 int i, end = end_option(optionptr);
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]);
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;
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)
156 char buffer[4]; /* Cant copy straight to optionptr, it might not be aligned */
158 for (i = 0; options[i].code; i++)
159 if (options[i].code == code) {
160 length = option_lengths[options[i].flags & TYPE_MASK];
165 DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
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;
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;
179 memcpy(&optionptr[end + 2], buffer, length);
180 optionptr[end + length + 2] = DHCP_END;
185 /* find option 'code' in opt_list */
186 struct option_set *find_option(struct option_set *opt_list, char code)
188 while (opt_list && opt_list->data[OPT_CODE] < code)
189 opt_list = opt_list->next;
191 if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
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)
199 struct option_set *existing, *new, **curr;
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 */
213 DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
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);
223 while (*curr && (*curr)->data[OPT_CODE] < option->code)
224 curr = &(*curr)->next;
233 void viInfoFree(pVI_OPTION_INFO pInfo)
237 free(pInfo->serialNumber);
238 free(pInfo->productClass);
243 void addViToList(pVI_OPTION_INFO pNew) {
244 pVI_OPTION_INFO pPtr= NULL;
246 /* if VI exists already, don't add, just update the info */
247 if (viList->count > 0) {
248 pPtr = viList->pHead;
250 if (pPtr->ipAddr == pNew->ipAddr) {
251 /* found it, just copy the info over */
252 memcpy(pPtr,pNew,sizeof(VI_OPTION_INFO));
258 } /* list has something */
260 if (viList->pHead == NULL) {
261 viList->pHead = pNew;
262 viList->pTail = pNew;
265 viList->pTail->next = pNew;
266 viList->pTail = pNew;
271 void viListFree(void)
273 pVI_OPTION_INFO pInfo;
275 while (viList->pHead) {
276 pInfo = viList->pHead;
277 viList->pHead = viList->pHead->next;
279 free(pInfo->serialNumber);
280 free(pInfo->productClass);
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)
294 char optionData[VENDOR_IDENTIFYING_INFO_LEN], *dataPtr;
295 int len, totalLen = 0;
296 char line[VENDOR_IDENTIFYING_INFO_LEN];
299 char productClass[64];
301 fs = fopen("/var/fyi/sys/info", "r");
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);
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 */
319 if (type == VENDOR_IDENTIFYING_FOR_DEVICE)
320 *dataPtr++ = (char)VENDOR_DEVICE_OUI_SUBCODE;
322 *dataPtr++ = (char)VENDOR_GATEWAY_OUI_SUBCODE;
325 strncpy(dataPtr,oui,len);
327 totalLen += (len + VENDOR_SUBCODE_AND_LEN_BYTES);
330 if (type == VENDOR_IDENTIFYING_FOR_DEVICE)
331 *dataPtr++ = (char)VENDOR_DEVICE_SERIAL_NUMBER_SUBCODE;
333 *dataPtr++ = (char)VENDOR_GATEWAY_SERIAL_NUMBER_SUBCODE;
334 len = strlen(serialNum);
336 strncpy(dataPtr,(const char*)serialNum,len);
338 totalLen += (len + VENDOR_SUBCODE_AND_LEN_BYTES);
341 if (type == VENDOR_IDENTIFYING_FOR_DEVICE)
342 *dataPtr++ = VENDOR_DEVICE_PRODUCT_CLASS_SUBCODE;
344 *dataPtr++ = VENDOR_GATEWAY_PRODUCT_CLASS_SUBCODE;
345 len = strlen(productClass);
347 strncpy(dataPtr,(const char*)productClass,len);
349 totalLen += (len + VENDOR_SUBCODE_AND_LEN_BYTES);
351 optionData[VENDOR_OPTION_LEN_OFFSET] = totalLen;
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));
358 /* udp_send and readIp from voice */
359 static int readIp(const char* ip)
368 res = (res << 8) | atoi(ip);
383 static int notifyApp(short port, void *data, int len)
386 struct sockaddr_in serv_addr;
387 struct sockaddr_in cli_addr;
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);
395 /* open udp socket */
396 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
398 printf("WT-104: Could not open socket for send\n");
399 return -1; /* could not open socket */
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);
408 if (bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0)
410 printf("dhcpd: Could not bind client socket\n");
411 return -2; /* could not bind client socket */
415 if (sendto(sockfd, data, len, 0, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) != len)
417 printf( "dhcpd: Could not sendto\n");
418 return -3; /* could not sendto */
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)
433 int subcodeParsed = 0;
435 pVI_OPTION_INFO pInfo = NULL;
439 if (option == NULL) {
440 DEBUG(LOG_ERR, "saveVIoption(): option is NULL.");
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;
451 while (subcodeParsed < maxSubcode) {
452 /* subcode, len, data */
453 subcode = *optionPtr++;
454 sublen = *optionPtr++;
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';
469 DEBUG(LOG_ERR, "saveVIoption(): subcode OUI, OUI len %d is too long.",sublen);
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';
484 DEBUG(LOG_ERR, "saveVIoption(): subcode SerialNumber, Serial Number len %d is too long.",sublen);
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';
499 DEBUG(LOG_ERR, "saveVIoption(): subcode ProductClass, Class len %d is too long.",sublen);
504 DEBUG(LOG_ERR, "saveVIoption(): subcode %d, not supported.",subcode);
508 /* add info to the manageable device link list */
509 } /* while subcodeParsed < maxSubcode */
511 notifyApp(30006,(void*)&msg,sizeof(msg));
520 if (pInfo->serialNumber)
521 free(pInfo->serialNumber);
522 if (pInfo->productClass)
523 free(pInfo->productClass);