]> git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/rfc2131.c
import of dnsmasq-2.6.tar.gz
[people/ms/dnsmasq.git] / src / rfc2131.c
1 /* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11 */
12
13 /* Author's email: simon@thekelleys.org.uk */
14
15 #include "dnsmasq.h"
16
17 #define BOOTREQUEST 1
18 #define BOOTREPLY 2
19 #define DHCP_COOKIE 0x63825363
20
21 #define OPTION_PAD 0
22 #define OPTION_NETMASK 1
23 #define OPTION_ROUTER 3
24 #define OPTION_DNSSERVER 6
25 #define OPTION_HOSTNAME 12
26 #define OPTION_DOMAINNAME 15
27 #define OPTION_BROADCAST 28
28 #define OPTION_REQUESTED_IP 50
29 #define OPTION_LEASE_TIME 51
30 #define OPTION_OVERLOAD 52
31 #define OPTION_MESSAGE_TYPE 53
32 #define OPTION_SERVER_IDENTIFIER 54
33 #define OPTION_REQUESTED_OPTIONS 55
34 #define OPTION_MESSAGE 56
35 #define OPTION_MAXMESSAGE 57
36 #define OPTION_T1 58
37 #define OPTION_T2 59
38 #define OPTION_CLIENT_ID 61
39 #define OPTION_END 255
40
41 #define DHCPDISCOVER 1
42 #define DHCPOFFER 2
43 #define DHCPREQUEST 3
44 #define DHCPDECLINE 4
45 #define DHCPACK 5
46 #define DHCPNAK 6
47 #define DHCPRELEASE 7
48 #define DHCPINFORM 8
49
50 static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
51 static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
52 static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname);
53 static int option_len(unsigned char *opt);
54 static void *option_ptr(unsigned char *opt);
55 static struct in_addr option_addr(unsigned char *opt);
56 static unsigned int option_uint(unsigned char *opt, int size);
57 static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
58 static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
59 static unsigned char *do_req_options(struct dhcp_context *context,
60 unsigned char *p, unsigned char *end,
61 unsigned char *req_options,
62 struct dhcp_opt *config_opts,
63 char *domainname, char *hostname,
64 struct in_addr router,
65 struct in_addr iface_addr,
66 int iface_mtu, char *netid);
67
68 static int have_config(struct dhcp_config *config, unsigned int mask)
69 {
70 return config && (config->flags & mask);
71 }
72
73 int dhcp_reply(struct dhcp_context *context,
74 struct in_addr iface_addr,
75 char *iface_name,
76 int iface_mtu,
77 struct udp_dhcp_packet *rawpacket,
78 unsigned int sz, time_t now, char *namebuff,
79 struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
80 char *domain_suffix, char *dhcp_file, char *dhcp_sname,
81 struct in_addr dhcp_next_server, struct in_addr router)
82 {
83 unsigned char *opt, *clid;
84 struct dhcp_lease *lease;
85 int clid_len;
86 struct dhcp_packet *mess = &rawpacket->data;
87 unsigned char *p = mess->options;
88 /* default max reply packet length, max be overridden */
89 unsigned char *end = (unsigned char *)(rawpacket + 1);
90 char *hostname = NULL;
91 char *req_options = NULL;
92 char *message = NULL;
93 unsigned int renewal_time, expires_time, def_time;
94 struct dhcp_config *config;
95 char *netid;
96
97 if (mess->op != BOOTREQUEST ||
98 mess->hlen != ETHER_ADDR_LEN ||
99 mess->cookie != htonl(DHCP_COOKIE))
100 return 0;
101
102 /* Token ring is supported when we have packet sockets
103 to make the HW headers for us. We don't have the code to build
104 token ring headers when using BPF. We rely on the fact that
105 token ring hwaddrs are the same size as ethernet hwaddrs. */
106
107 #ifdef HAVE_BPF
108 if (mess->htype != ARPHRD_ETHER)
109 return 0;
110 #else
111 if (mess->htype != ARPHRD_ETHER &&
112 mess->htype != ARPHRD_IEEE802)
113 return 0;
114 #endif
115
116 mess->op = BOOTREPLY;
117
118 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE)))
119 {
120 int maxsize = (int)option_uint(opt, 2);
121 if (maxsize > DNSMASQ_PACKETSZ)
122 maxsize = DNSMASQ_PACKETSZ;
123 if (maxsize > iface_mtu)
124 maxsize = iface_mtu;
125
126 end = ((unsigned char *)rawpacket) + maxsize;
127 }
128
129 /* If there is no client identifier option, use the hardware address */
130 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
131 {
132 clid = option_ptr(opt);
133 clid_len = option_len(opt);
134 }
135 else
136 {
137 clid = mess->chaddr;
138 clid_len = 0;
139 }
140
141 /* do we have a lease in store? */
142 lease = lease_find_by_client(clid, clid_len);
143
144 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
145 {
146 int len = option_len(opt);
147 req_options = namebuff;
148 memcpy(req_options, option_ptr(opt), len);
149 req_options[len] = OPTION_END;
150 }
151
152 if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) &&
153 have_config(config, CONFIG_NAME))
154 hostname = config->hostname;
155 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
156 {
157 int len = option_len(opt);
158 /* namebuff is 1K long, use half for requested options and half for hostname */
159 /* len < 256 by definition */
160 hostname = namebuff + 500;
161 memcpy(hostname, option_ptr(opt), len);
162 /* May not be zero terminated */
163 hostname[len] = 0;
164 /* ensure there are no strange chars in there */
165 if (!canonicalise(hostname))
166 hostname = NULL;
167 else
168 {
169 char *dot = strchr(hostname, '.');
170 if (dot)
171 {
172 if (!domain_suffix || !hostname_isequal(dot+1, domain_suffix))
173 {
174 syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
175 hostname = NULL;
176 }
177 else
178 *dot = 0; /* truncate */
179 }
180 }
181 }
182
183 /* search again now we have a hostname */
184 config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname);
185 def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
186 netid = have_config(config, CONFIG_NETID) ? config->netid : context->netid;
187
188 if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
189 {
190 unsigned int req_time = option_uint(opt, 4);
191
192 if (def_time == 0xffffffff ||
193 (req_time != 0xffffffff && req_time < def_time))
194 expires_time = renewal_time = req_time;
195 else
196 expires_time = renewal_time = def_time;
197 }
198 else
199 {
200 renewal_time = def_time;
201 if (lease)
202 expires_time = (unsigned int)difftime(lease->expires, now);
203 else
204 expires_time = def_time;
205 }
206
207 if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
208 return 0;
209
210 switch (opt[2])
211 {
212 case DHCPDECLINE:
213 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
214 (iface_addr.s_addr != option_addr(opt).s_addr))
215 return 0;
216
217 /* sanitise any message. Paranoid? Moi? */
218 if ((opt = option_find(mess, sz, OPTION_MESSAGE)))
219 {
220 char *p = option_ptr(opt), *q = namebuff;
221 int i;
222
223 for (i = option_len(opt); i > 0; i--)
224 {
225 char c = *p++;
226 if (isprint(c))
227 *q++ = c;
228 }
229 *q++ = 0; /* add terminator */
230 message = namebuff;
231 }
232
233 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
234 return 0;
235
236 log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message);
237
238 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
239 lease_prune(lease, now);
240
241 if (have_config(config, CONFIG_ADDR) &&
242 config->addr.s_addr == option_addr(opt).s_addr)
243 {
244 syslog(LOG_WARNING, "disabling DHCP static address %s", inet_ntoa(config->addr));
245 config->flags &= ~CONFIG_ADDR ;
246 }
247
248 return 0;
249
250 case DHCPRELEASE:
251 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
252 (iface_addr.s_addr != option_addr(opt).s_addr))
253 return 0;
254
255 log_packet("RELEASE", &mess->ciaddr, mess->chaddr, iface_name, NULL);
256
257 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
258 lease_prune(lease, now);
259
260 return 0;
261
262 case DHCPDISCOVER:
263 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
264 mess->yiaddr = option_addr(opt);
265
266 if (have_config(config, CONFIG_DISABLE))
267 message = "ignored";
268 else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr))
269 mess->yiaddr = config->addr;
270 else if (lease &&
271 ((lease->addr.s_addr & context->netmask.s_addr) ==
272 (context->start.s_addr & context->netmask.s_addr)))
273 mess->yiaddr = lease->addr;
274 else if ((!opt || !address_available(context, mess->yiaddr)) &&
275 !address_allocate(context, dhcp_configs, &mess->yiaddr))
276 message = "no address available";
277
278 log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, message);
279 if (message)
280 return 0;
281
282 bootp_option_put(mess, dhcp_file, dhcp_sname);
283 mess->siaddr = dhcp_next_server;
284 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
285 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
286 p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
287 p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
288 NULL, router, iface_addr, iface_mtu, netid);
289 p = option_put(p, end, OPTION_END, 0, 0);
290
291 log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
292 return p - (unsigned char *)mess;
293
294
295 case DHCPREQUEST:
296 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
297 {
298 /* SELECTING or INIT_REBOOT */
299 mess->yiaddr = option_addr(opt);
300 /* The RFC says that this is already zero, but there exist
301 real-world counter examples. */
302 mess->ciaddr.s_addr = 0;
303
304 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) &&
305 (iface_addr.s_addr != option_addr(opt).s_addr))
306 return 0;
307
308 /* If a lease exists for this host and another address, squash it. */
309 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
310 {
311 lease_prune(lease, now);
312 lease = NULL;
313 }
314
315 /* accept addresses in the dynamic range or ones allocated statically to
316 particular hosts or an address which the host already has. */
317 if (!lease)
318 {
319 if (!address_available(context, mess->yiaddr) &&
320 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
321 message = "address unavailable";
322 else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
323 message = "no leases left";
324 }
325 }
326 else
327 {
328 /* RENEWING or REBINDING */
329 /* Must exist a lease for this address */
330 if (!mess->ciaddr.s_addr)
331 return 0;
332
333 mess->yiaddr = mess->ciaddr;
334 if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
335 message = "lease not found";
336 }
337
338 /* If a machine moves networks whilst it has a lease, we catch that here. */
339 if ((mess->yiaddr.s_addr & context->netmask.s_addr) != (context->start.s_addr & context->netmask.s_addr))
340 message = "wrong network";
341
342 if (have_config(config, CONFIG_DISABLE))
343 message = "disabled";
344
345 log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
346
347 if (message)
348 {
349 log_packet("NAK", &mess->yiaddr, mess->chaddr, iface_name, message);
350
351 mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
352 bootp_option_put(mess, NULL, NULL);
353 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
354 p = option_put_string(p, end, OPTION_MESSAGE, message);
355 p = option_put(p, end, OPTION_END, 0, 0);
356 mess->flags |= htons(0x8000); /* broadcast */
357 return p - (unsigned char *)mess;
358 }
359
360 log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
361
362 lease_set_hwaddr(lease, mess->chaddr);
363 lease_set_hostname(lease, hostname, domain_suffix);
364 lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
365
366 bootp_option_put(mess, dhcp_file, dhcp_sname);
367 mess->siaddr = dhcp_next_server;
368 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
369 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
370 p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
371 if (renewal_time != 0xffffffff)
372 {
373 unsigned short fuzz = rand16();
374 while (fuzz > (renewal_time/16))
375 fuzz = fuzz/2;
376 p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
377 p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
378 }
379 p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
380 hostname, router, iface_addr, iface_mtu, netid);
381 p = option_put(p, end, OPTION_END, 0, 0);
382 return p - (unsigned char *)mess;
383
384 case DHCPINFORM:
385 if (have_config(config, CONFIG_DISABLE))
386 {
387 log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, "ignored");
388 return 0;
389 }
390
391 log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, NULL);
392
393 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
394 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
395 p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
396 hostname, router, iface_addr, iface_mtu, netid);
397 p = option_put(p, end, OPTION_END, 0, 0);
398
399 log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
400 return p - (unsigned char *)mess;
401 }
402
403 return 0;
404 }
405
406 static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string)
407 {
408 syslog(LOG_INFO, "DHCP%s(%s)%s%s %.2x:%.2x:%.2x:%.2x:%.2x:%.2x%s%s",
409 type,
410 interface,
411 addr ? " " : "",
412 addr ? inet_ntoa(*addr) : "",
413 hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5],
414 string ? " " : "",
415 string ? string : "");
416 }
417
418 static int option_len(unsigned char *opt)
419 {
420 return opt[1];
421 }
422
423 static void *option_ptr(unsigned char *opt)
424 {
425 return &opt[2];
426 }
427
428 static struct in_addr option_addr(unsigned char *opt)
429 {
430 /* this worries about unaligned data in the option. */
431 /* struct in_addr is network byte order */
432 struct in_addr ret;
433
434 memcpy(&ret, option_ptr(opt), INADDRSZ);
435
436 return ret;
437 }
438
439 static unsigned int option_uint(unsigned char *opt, int size)
440 {
441 /* this worries about unaligned data and byte order */
442 unsigned int ret = 0;
443 int i;
444 unsigned char *p = option_ptr(opt);
445
446 for (i = 0; i < size; i++)
447 ret = (ret << 8) | *p++;
448
449 return ret;
450 }
451
452 static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname)
453 {
454 memset(mess->sname, 0, sizeof(mess->sname));
455 memset(mess->file, 0, sizeof(mess->file));
456 if (sname)
457 strncpy(mess->sname, sname, sizeof(mess->sname)-1);
458 if (filename)
459 strncpy(mess->file, filename, sizeof(mess->file)-1);
460 }
461
462 static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
463 {
464 int i;
465
466 /* always keep one octet space for the END option. */
467 if ((opt == OPTION_END) || (p + len + 3 < end))
468 {
469 *(p++) = opt;
470 if (opt != OPTION_END)
471 {
472 *(p++) = len;
473
474 for (i = 0; i < len; i++)
475 *(p++) = val >> (8 * (len - (i + 1)));
476 }
477 }
478 return p;
479 }
480
481 static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
482 {
483 if (p + strlen(string) + 3 < end)
484 {
485 *(p++) = opt;
486 *(p++) = strlen(string);
487 memcpy(p, string, strlen(string));
488 p += strlen(string);
489 }
490 return p;
491 }
492
493 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload)
494 {
495 if (!p)
496 return NULL;
497
498 while (*p != OPTION_END)
499 {
500 if (end && (p >= end))
501 return 0; /* malformed packet */
502 else if (*p == OPTION_PAD)
503 p++;
504 else if (*p == OPTION_OVERLOAD)
505 {
506 if (end && (p >= end - 3))
507 return 0; /* malformed packet */
508 if (overload)
509 *overload = *(p+2);
510 p += 3;
511 }
512 else
513 {
514 int opt_len;;
515 if (end && (p >= end - 2))
516 return 0; /* malformed packet */
517 opt_len = option_len(p);
518 if (end && (p >= end - (2 + opt_len)))
519 return 0; /* malformed packet */
520 if (*p == opt)
521 return p;
522 p += opt_len + 2;
523 }
524 }
525
526 return NULL;
527 }
528
529 static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type)
530 {
531 int overload = 0;
532 unsigned char *ret;
533
534 ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, &overload);
535
536 if (!ret && (overload & 1))
537 ret = option_find1(&mess->file[0], &mess->file[128], opt_type, &overload);
538
539 if (!ret && (overload & 2))
540 ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload);
541
542 return ret;
543 }
544
545 static int in_list(unsigned char *list, int opt)
546 {
547 int i;
548
549 for (i = 0; list[i] != OPTION_END; i++)
550 if (opt == list[i])
551 return 1;
552
553 return 0;
554 }
555
556 static struct dhcp_opt *option_find2(char *netid, struct dhcp_opt *opts, int opt)
557 {
558 for (; opts; opts = opts->next)
559 if (opts->opt == opt &&
560 (!opts->netid || (netid && strcmp(opts->netid, netid) == 0)))
561 return opts;
562 return NULL;
563 }
564
565 static unsigned char *do_req_options(struct dhcp_context *context,
566 unsigned char *p, unsigned char *end,
567 unsigned char *req_options,
568 struct dhcp_opt *config_opts,
569 char *domainname, char *hostname,
570 struct in_addr router,
571 struct in_addr iface_addr,
572 int iface_mtu, char *netid)
573 {
574 int i;
575
576 if (!req_options)
577 return p;
578
579 if (in_list(req_options, OPTION_MAXMESSAGE))
580 p = option_put(p, end, OPTION_MAXMESSAGE, 2,
581 DNSMASQ_PACKETSZ > iface_mtu ?
582 iface_mtu : DNSMASQ_PACKETSZ);
583
584 if (in_list(req_options, OPTION_NETMASK) &&
585 !option_find2(netid, config_opts, OPTION_NETMASK))
586 p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
587
588 if (in_list(req_options, OPTION_BROADCAST) &&
589 !option_find2(netid, config_opts, OPTION_BROADCAST))
590 p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
591
592 if (in_list(req_options, OPTION_ROUTER) &&
593 !option_find2(netid, config_opts, OPTION_ROUTER))
594 p = option_put(p, end, OPTION_ROUTER, INADDRSZ,
595 ntohl(router.s_addr));
596
597 if (in_list(req_options, OPTION_DNSSERVER) &&
598 !option_find2(netid, config_opts, OPTION_DNSSERVER))
599 p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
600
601 if (domainname && in_list(req_options, OPTION_DOMAINNAME) &&
602 !option_find2(netid, config_opts, OPTION_DOMAINNAME))
603 p = option_put_string(p, end, OPTION_DOMAINNAME, domainname);
604
605 /* Note that we ignore attempts to set the hostname using
606 --dhcp-option=12,<name> */
607 if (hostname && in_list(req_options, OPTION_HOSTNAME))
608 p = option_put_string(p, end, OPTION_HOSTNAME, hostname);
609
610 for (i = 0; req_options[i] != OPTION_END; i++)
611 {
612 struct dhcp_opt *opt;
613
614 if (req_options[i] == OPTION_HOSTNAME ||
615 req_options[i] == OPTION_MAXMESSAGE ||
616 !(opt = option_find2(netid, config_opts, req_options[i])) ||
617 (p + opt->len + 3 >= end))
618 continue;
619
620 /* For the options we have default values on
621 dhc-option=<optionno> means "don't include this option"
622 not "include a zero-length option" */
623 if (opt->len == 0 &&
624 (opt->opt == OPTION_NETMASK ||
625 opt->opt == OPTION_BROADCAST ||
626 opt->opt == OPTION_ROUTER ||
627 opt->opt == OPTION_DNSSERVER))
628 continue;
629
630 *(p++) = opt->opt;
631 *(p++) = opt->len;
632 if (opt->len == 0)
633 continue;
634
635 if (opt->is_addr)
636 {
637 int j;
638 struct in_addr *a = (struct in_addr *)opt->val;
639 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
640 {
641 /* zero means "self" */
642 if (a->s_addr == 0)
643 memcpy(p, &iface_addr, INADDRSZ);
644 else
645 memcpy(p, a, INADDRSZ);
646 p += INADDRSZ;
647 }
648 }
649 else
650 {
651 memcpy(p, opt->val, opt->len);
652 p += opt->len;
653 }
654 }
655 return p;
656 }
657
658