On Fri, Dec 09, 2016 at 10:08:09AM +0100, Rafael Zalamena wrote:
> On Thu, Dec 08, 2016 at 08:43:20PM +0100, Rafael Zalamena wrote:
> > This diff implements layer 2 relaying support for dhcrelay with further
> > support for Relay Agent Info (RFC 3046). This feature is mostly used by
> > switched networks that might not be using IP addresses when in the edge
> > with the customer.
> >
> > Basically this diff allows you to run dhcrelay on interfaces without
> > addresses and doesn't require you to specify an DHCP server address.
> > Instead you just need to specify the output port.
> >
> > I also updated the man page to show the new options for layer 2 relaying
> > Relay Agent Info knobs, since you might want to let the remote DHCP
> > server know where the DHCP packet is coming from.
>
> I forgot to add the man page in the last diff, here is a new one with
> the man page modifications.
>
> ok?
>
See comments below.
Reyk
> Index: bpf.c
> ===================================================================
> RCS file: /home/obsdcvs/src/usr.sbin/dhcrelay/bpf.c,v
> retrieving revision 1.13
> diff -u -p -r1.13 bpf.c
> --- bpf.c 8 Dec 2016 19:18:15 -0000 1.13
> +++ bpf.c 9 Dec 2016 09:03:44 -0000
> @@ -93,6 +93,38 @@ if_register_send(struct interface_info *
> }
>
> /*
> + * Packet filter program: 'ip and udp and dst port CLIENT_PORT'
> + */
> +struct bpf_insn dhcp_bpf_sfilter[] = {
> + /* Make sure this is an IP packet... */
> + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
> + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
> +
> + /* Make sure it's a UDP packet... */
> + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
> + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
> +
> + /* Make sure this isn't a fragment... */
> + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
> + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
> +
> + /* Get the IP header length... */
> + BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
> +
> + /* Make sure it's to the right port... */
> + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
> + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, CLIENT_PORT, 0, 1),
> +
> + /* If we passed all the tests, ask for the whole packet. */
> + BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
> +
> + /* Otherwise, drop it. */
> + BPF_STMT(BPF_RET+BPF_K, 0),
> +};
> +
> +int dhcp_bpf_sfilter_len = sizeof(dhcp_bpf_sfilter) / sizeof(struct
> bpf_insn);
> +
> +/*
> * Packet filter program: 'ip and udp and dst port SERVER_PORT'
> */
> struct bpf_insn dhcp_bpf_filter[] = {
> @@ -161,6 +193,38 @@ struct bpf_insn dhcp_bpf_efilter[] = {
> int dhcp_bpf_efilter_len = sizeof(dhcp_bpf_efilter) / sizeof(struct
> bpf_insn);
>
> /*
> + * Packet write filter program: 'ip and udp and src port CLIENT_PORT'
> + */
> +struct bpf_insn dhcp_bpf_swfilter[] = {
> + /* Make sure this is an IP packet... */
> + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
> + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
> +
> + /* Make sure it's a UDP packet... */
> + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
> + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
> +
> + /* Make sure this isn't a fragment... */
> + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
> + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
> +
> + /* Get the IP header length... */
> + BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
> +
> + /* Make sure it's from the right port... */
> + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
> + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, CLIENT_PORT, 0, 1),
> +
> + /* If we passed all the tests, ask for the whole packet. */
> + BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
> +
> + /* Otherwise, drop it. */
> + BPF_STMT(BPF_RET+BPF_K, 0),
> +};
> +
> +int dhcp_bpf_swfilter_len = sizeof(dhcp_bpf_swfilter) / sizeof(struct
> bpf_insn);
> +
> +/*
> * Packet write filter program: 'ip and udp and src port SERVER_PORT'
> */
> struct bpf_insn dhcp_bpf_wfilter[] = {
> @@ -193,7 +257,7 @@ struct bpf_insn dhcp_bpf_wfilter[] = {
> int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct
> bpf_insn);
>
> void
> -if_register_receive(struct interface_info *info)
> +if_register_receive(struct interface_info *info, int isserver)
> {
> struct bpf_version v;
> struct bpf_program p;
> @@ -234,7 +298,10 @@ if_register_receive(struct interface_inf
> info->rbuf_len = 0;
>
> /* Set up the bpf filter program structure. */
> - if (info->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
> + if (isserver) {
> + p.bf_len = dhcp_bpf_sfilter_len;
> + p.bf_insns = dhcp_bpf_sfilter;
> + } else if (info->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
> p.bf_len = dhcp_bpf_efilter_len;
> p.bf_insns = dhcp_bpf_efilter;
> } else {
> @@ -245,8 +312,13 @@ if_register_receive(struct interface_inf
> error("Can't install packet filter program: %m");
>
> /* Set up the bpf write filter program structure. */
> - p.bf_len = dhcp_bpf_wfilter_len;
> - p.bf_insns = dhcp_bpf_wfilter;
> + if (isserver) {
> + p.bf_len = dhcp_bpf_swfilter_len;
> + p.bf_insns = dhcp_bpf_swfilter;
> + } else {
> + p.bf_len = dhcp_bpf_wfilter_len;
> + p.bf_insns = dhcp_bpf_wfilter;
> + }
>
> if (ioctl(info->rfdesc, BIOCSETWF, &p) == -1)
> error("Can't install write filter program: %m");
> Index: dhcpd.h
> ===================================================================
> RCS file: /home/obsdcvs/src/usr.sbin/dhcrelay/dhcpd.h,v
> retrieving revision 1.17
> diff -u -p -r1.17 dhcpd.h
> --- dhcpd.h 8 Dec 2016 19:18:15 -0000 1.17
> +++ dhcpd.h 9 Dec 2016 09:03:44 -0000
> @@ -39,6 +39,12 @@
> * Enterprises, see ``http://www.vix.com''.
> */
>
> +enum dhcp_relay_mode {
> + DRM_UNKNOWN,
> + DRM_LAYER2,
> + DRM_LAYER3,
> +};
> +
This looks a bit misplaced here, you should move it below "enum dhcp_state".
> #define SERVER_PORT 67
> #define CLIENT_PORT 68
>
> @@ -123,7 +129,7 @@ int debug(char *, ...) __attribute__ ((_
> /* bpf.c */
> int if_register_bpf(struct interface_info *);
> void if_register_send(struct interface_info *);
> -void if_register_receive(struct interface_info *);
> +void if_register_receive(struct interface_info *, int);
> ssize_t send_packet(struct interface_info *,
> struct dhcp_packet *, size_t, struct packet_ctx *);
> ssize_t receive_packet(struct interface_info *, unsigned char *, size_t,
> @@ -133,7 +139,7 @@ ssize_t receive_packet(struct interface_
> extern void (*bootp_packet_handler)(struct interface_info *,
> struct dhcp_packet *, int, struct packet_ctx *);
> struct interface_info *get_interface(const char *,
> - void (*)(struct protocol *));
> + void (*)(struct protocol *), int isserver);
> void dispatch(void);
> void got_one(struct protocol *);
> void add_protocol(char *, int, void (*)(struct protocol *), void *);
> Index: dhcrelay.8
> ===================================================================
> RCS file: /home/obsdcvs/src/usr.sbin/dhcrelay/dhcrelay.8,v
> retrieving revision 1.12
> diff -u -p -r1.12 dhcrelay.8
> --- dhcrelay.8 16 Jul 2013 11:13:33 -0000 1.12
> +++ dhcrelay.8 9 Dec 2016 09:05:13 -0000
> @@ -45,6 +45,8 @@
> .Sh SYNOPSIS
> .Nm
> .Op Fl do
> +.Op Fl C Ar circuit-id
> +.Op Fl R Ar remote-id
These two options need a description under "The options are as follows:"
.It Fl C Ar circuit-id
...
.It Fl R Ar remote-id
...
> .Fl i Ar interface
> .Ar server1 Op Ar ... serverN
> .Sh DESCRIPTION
> @@ -62,6 +64,11 @@ forwards it to the list of DHCP servers
> When a reply is received, it is broadcast or unicast on the network from
> whence the original request came.
> .Pp
> +The server might be a name, address or interface.
> +.Nm
> +will operate in layer 2 mode when the specified servers are interfaces,
> +otherwise it will operate in layer 3 mode.
> +.Pp
> The name of at least one DHCP server to which DHCP and BOOTP requests
> should be relayed,
> as well as the name of the network interface that
> @@ -73,7 +80,7 @@ must be specified on the command line.
> supports relaying of DHCP traffic to configure IPsec tunnel mode
> clients when listening on the
> .Xr enc 4
> -interface.
> +interface using layer 3 mode only.
> The DHCP server has to support RFC 3046 to echo back the relay agent
> information to allow stateless DHCP reply to IPsec tunnel mapping.
> .Pp
> Index: dhcrelay.c
> ===================================================================
> RCS file: /home/obsdcvs/src/usr.sbin/dhcrelay/dhcrelay.c,v
> retrieving revision 1.49
> diff -u -p -r1.49 dhcrelay.c
> --- dhcrelay.c 8 Dec 2016 19:18:15 -0000 1.49
> +++ dhcrelay.c 9 Dec 2016 09:03:44 -0000
> @@ -66,6 +66,8 @@ void usage(void);
> int rdaemon(int);
> void relay(struct interface_info *, struct dhcp_packet *, int,
> struct packet_ctx *);
> +void l2relay(struct interface_info *, struct dhcp_packet *, int,
> + struct packet_ctx *);
> char *print_hw_addr(int, int, unsigned char *);
> void got_response(struct protocol *);
> int get_rdomain(char *);
> @@ -84,7 +86,12 @@ struct interface_info *interfaces = NULL
> int server_fd;
> int oflag;
>
> +enum dhcp_relay_mode drm = DRM_UNKNOWN;
> +const char *rai_circuit = NULL;
> +const char *rai_remote = NULL;
> +
> struct server_list {
> + struct interface_info *intf;
> struct server_list *next;
> struct sockaddr_in to;
> int fd;
> @@ -98,6 +105,7 @@ main(int argc, char *argv[])
> struct server_list *sp = NULL;
> struct passwd *pw;
> struct sockaddr_in laddr;
> + int optslen;
>
> daemonize = 1;
>
> @@ -105,8 +113,11 @@ main(int argc, char *argv[])
> openlog(__progname, LOG_NDELAY, DHCPD_LOG_FACILITY);
> setlogmask(LOG_UPTO(LOG_INFO));
>
> - while ((ch = getopt(argc, argv, "adi:o")) != -1) {
> + while ((ch = getopt(argc, argv, "aC:di:oR:")) != -1) {
> switch (ch) {
> + case 'C':
> + rai_circuit = optarg;
> + break;
> case 'd':
> daemonize = 0;
> break;
> @@ -114,7 +125,7 @@ main(int argc, char *argv[])
> if (interfaces != NULL)
> usage();
>
> - interfaces = get_interface(optarg, got_one);
> + interfaces = get_interface(optarg, got_one, 0);
> if (interfaces == NULL)
> error("interface '%s' not found", optarg);
> break;
> @@ -122,6 +133,9 @@ main(int argc, char *argv[])
> /* add the relay agent information option */
> oflag++;
> break;
> + case 'R':
> + rai_remote = optarg;
> + break;
>
> default:
> usage();
> @@ -135,10 +149,42 @@ main(int argc, char *argv[])
> if (argc < 1)
> usage();
>
> + if (rai_remote != NULL && rai_circuit == NULL)
> + error("you must specify a circuit-id with a remote-id");
For the circuit-id, you could default to the interface name or index
where the packet was received on. The remote-id could even default to
the an hostname or address. And how does it differ from the existing
-o (see below)?
> +
> + /* Validate that we have space for all suboptions. */
> + if (rai_circuit != NULL) {
> + optslen = 2 + strlen(rai_circuit);
> + if (rai_remote != NULL)
> + optslen += 2 + strlen(rai_remote);
> +
> + if (optslen > 255)
> + error("relay agent information is too long");
> + }
> +
> while (argc > 0) {
> struct hostent *he;
> struct in_addr ia, *iap = NULL;
>
> + if ((sp = calloc(1, sizeof(*sp))) == NULL)
> + error("calloc");
> +
> + if ((sp->intf = get_interface(argv[0], got_one, 1)) != NULL) {
> + if (drm == DRM_LAYER3)
> + error("don't mix interfaces with hosts");
> +
> + if (sp->intf->hw_address.htype == HTYPE_IPSEC_TUNNEL)
> + error("can't use IPSec with layer 2");
> +
> + sp->next = servers;
> + servers = sp;
> +
> + drm = DRM_LAYER2;
> + argc--;
> + argv++;
> + continue;
> + }
> +
> if (inet_aton(argv[0], &ia))
> iap = &ia;
> else {
> @@ -149,12 +195,16 @@ main(int argc, char *argv[])
> iap = ((struct in_addr *)he->h_addr_list[0]);
> }
> if (iap) {
> - if ((sp = calloc(1, sizeof *sp)) == NULL)
> - error("calloc");
> + if (drm == DRM_LAYER2)
> + error("don't mix interfaces with hosts");
> +
> + drm = DRM_LAYER3;
> sp->next = servers;
> servers = sp;
> memcpy(&sp->to.sin_addr, iap, sizeof *iap);
> - }
> + } else
> + free(sp);
> +
> argc--;
> argv++;
> }
> @@ -167,7 +217,9 @@ main(int argc, char *argv[])
>
> if (interfaces == NULL)
> error("no interface given");
> - if (interfaces->primary_address.s_addr == 0)
> + /* We need an address for running layer 3 mode. */
> + if (drm == DRM_LAYER3 &&
> + interfaces->primary_address.s_addr == 0)
> error("interface '%s' does not have an address",
> interfaces->name);
>
> @@ -192,6 +244,9 @@ main(int argc, char *argv[])
> laddr.sin_addr.s_addr = interfaces->primary_address.s_addr;
> /* Set up the server sockaddrs. */
> for (sp = servers; sp; sp = sp->next) {
> + if (sp->intf != NULL)
> + break;
> +
> sp->to.sin_port = server_port;
> sp->to.sin_family = AF_INET;
> sp->to.sin_len = sizeof sp->to;
> @@ -234,7 +289,10 @@ main(int argc, char *argv[])
> tzset();
>
> time(&cur_time);
> - bootp_packet_handler = relay;
> + if (drm == DRM_LAYER3)
> + bootp_packet_handler = relay;
> + else
> + bootp_packet_handler = l2relay;
>
> if ((pw = getpwnam("_dhcp")) == NULL)
> error("user \"_dhcp\" not found");
> @@ -374,7 +432,8 @@ usage(void)
> {
> extern char *__progname;
>
> - fprintf(stderr, "usage: %s [-do] -i interface server1 [... serverN]\n",
> + fprintf(stderr, "usage: %s [-CdoR] "
> + "-i interface server1 [... serverN]\n",
"server" can now be an interface name as well. Is there a better name?
destination?
> __progname);
> exit(1);
> }
> @@ -598,4 +657,259 @@ get_rdomain(char *name)
>
> close(s);
> return rv;
> +}
> +
> +ssize_t
> +relayagentinfo_append(struct dhcp_packet *dp, size_t dplen)
> +{
> + uint8_t *p;
> + ssize_t newtotal = dplen;
> + size_t opttotal;
> + int optlen, i, hasinfo = 0;
> + int circuitlen, remotelen;
> +
> + if (rai_circuit == NULL)
> + return (dplen);
> +
> + p = (uint8_t *)&dp->options;
> + if (memcmp(p, DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN)) {
> + note("invalid dhcp options cookie");
> + return (-1);
> + }
> +
> + p += DHCP_OPTIONS_COOKIE_LEN;
> + opttotal = dplen - DHCP_FIXED_NON_UDP - DHCP_OPTIONS_COOKIE_LEN;
> +
> + for (i = 0; i < opttotal && *p != DHO_END;) {
you're comparing signed i with unsigned opttotal here and in a few
other places below.
> + if (*p == DHO_PAD)
> + optlen = 1;
> + else
> + optlen = p[1] + 2;
> +
> + if ((i + optlen) > opttotal) {
> + note("truncated dhcp options");
> + return (-1);
> + }
> +
> + if (*p == DHO_RELAY_AGENT_INFORMATION) {
> + hasinfo = 1;
> + continue;
> + }
> +
> + p += optlen;
> + i += optlen;
> +
> + /* We reached the end, append the relay agent info. */
> + if (*p == DHO_END || i >= opttotal) {
> + /* We already have the Relay Agent Info, skip it. */
> + if (hasinfo)
> + continue;
> +
> + circuitlen = strlen(rai_circuit);
> + if (rai_remote != NULL)
> + remotelen = strlen(rai_remote);
> + else
> + remotelen = 0;
> +
> + *p++ = DHO_RELAY_AGENT_INFORMATION;
I do not see where you're checking the remaining space in dp->options;
Isn't opttotal the limit, and if i >= opttotal or all the *p++ don't
for the agent info don't fit? relay_agentinfo() checks if there is
enough space for the agent information.
> + if (rai_remote != NULL)
> + *p++ = (2 + circuitlen) + (2 + remotelen);
> + else
> + *p++ = (2 + circuitlen);
> +
> + newtotal += 2;
> +
> + *p++ = RAI_CIRCUIT_ID;
> + *p++ = circuitlen;
> + memcpy(p, rai_circuit, circuitlen);
> + p += circuitlen;
> + newtotal += 2 + circuitlen;
> +
The encoding is different to the DHO_RELAY_AGENT_INFORMATION (option
82) that I added for enc0/IPsec where the circuit-id is just the
interface index and the remote id an IP address. I know this is
related to the other standard, but could this be merged with
relay_agentinfo() somehow or documented that there is a difference?
> + if (rai_remote != NULL) {
> + *p++ = RAI_REMOTE_ID;
> + *p++ = remotelen;
> + memcpy(p, rai_remote, remotelen);
> + p += remotelen;
> + newtotal += 2 + remotelen;
> + }
> +
> + *p = DHO_END;
> + }
> + }
> +
> + return (newtotal);
> +}
> +
> +int
> +relayagentinfo_cmp(uint8_t *p, int plen)
> +{
plen is not used in this function, so there is no bounds checking for
the memcpys.
> + int len;
> + char buf[256];
> +
> + switch (*p) {
> + case RAI_CIRCUIT_ID:
> + len = *(p + 1);
> + memcpy(buf, p + 2, len);
> + buf[len + 1] = 0;
> +
> + return (strcmp(rai_circuit, buf));
> +
> + case RAI_REMOTE_ID:
> + len = *(p + 1);
> + memcpy(buf, p + 2, len);
> + buf[len + 1] = 0;
> +
> + return (strcmp(rai_circuit, buf));
> +
> + default:
> + /* Unmatched type */
> + note("unmatched relay info %d", *p);
> + return (0);
> + }
> +}
> +
> +ssize_t
> +relayagentinfo_remove(struct dhcp_packet *dp, size_t dplen)
> +{
> + uint8_t *p, *np, *startp, *endp;
> + size_t opttotal, optleft;
> + int suboptlen, optlen, i;
> + int remaining, matched = 0;
> +
> + startp = (uint8_t *)dp;
> + p = (uint8_t *)&dp->options;
> + if (memcmp(p, DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN)) {
> + note("invalid dhcp options cookie");
> + return (-1);
> + }
> +
> + opttotal = dplen - DHCP_FIXED_NON_UDP - DHCP_OPTIONS_COOKIE_LEN;
> + optleft = opttotal;
> +
> + p += DHCP_OPTIONS_COOKIE_LEN;
> + endp = p + opttotal;
> +
> + for (i = 0; i < opttotal && *p != DHO_END;) {
> + if (*p == DHO_PAD)
> + optlen = 1;
> + else
> + optlen = p[1] + 2;
> +
> + if ((i + optlen) > opttotal) {
> + note("truncated dhcp options");
> + return (-1);
> + }
> +
> + if (*p == DHO_RELAY_AGENT_INFORMATION) {
> + /* Fast case: there is no next option. */
> + np = p + optlen;
> + if (*np == DHO_END) {
> + *p = *np;
> + endp = p + 1;
> + return (endp - startp);
> + }
> +
> + remaining = optlen;
> + while (remaining > 0) {
> + suboptlen = *(p + 1);
> + remaining -= 2 + suboptlen;
> +
> + matched = 1;
> + if (relayagentinfo_cmp(p, suboptlen) == 0)
Yeah, you should check suboptlen/plen in relayagentinfo_cmp.
> + continue;
> +
> + matched = 0;
> + break;
> + }
> + /* It is not ours Relay Agent Info, don't remove it. */
> + if (matched == 0)
> + break;
> +
> + /* Move the other options on top of this one. */
> + optleft -= optlen;
> + endp -= optlen;
> +
> + /* Replace the old agent relay info. */
> + memmove(p, dp, optleft);
> +
> + return (endp - p);
> + }
> +
> + p += optlen;
> + i += optlen;
> + optleft -= optlen;
> + }
> +
> + return (endp - startp);
> +}
> +
> +void
> +l2relay(struct interface_info *ip, struct dhcp_packet *dp, int length,
> + struct packet_ctx *pc)
> +{
> + struct server_list *sp;
> + ssize_t dplen;
> +
> + if (dp->hlen > sizeof(dp->chaddr)) {
> + note("Discarding packet with invalid hlen.");
> + return;
> + }
> +
> + switch (dp->op) {
> + case BOOTREQUEST:
> + /* Add the relay agent info asked by the user. */
> + if ((dplen = relayagentinfo_append(dp, length)) == -1)
> + return;
> +
> + /*
> + * Re-send the packet to every interface except the one
> + * it came in.
> + */
> + for (sp = servers; sp != NULL; sp = sp->next) {
> + if (sp->intf == ip)
> + continue;
> +
> + debug("forwarded BOOTREQUEST for %s to %s",
> + print_hw_addr(pc->pc_htype, pc->pc_hlen,
> + pc->pc_smac), sp->intf->name);
> +
> + send_packet(sp->intf, dp, dplen, pc);
> + }
> + if (ip != interfaces) {
> + debug("forwarded BOOTREQUEST for %s to %s",
> + print_hw_addr(pc->pc_htype, pc->pc_hlen,
> + pc->pc_smac), interfaces->name);
> +
> + send_packet(interfaces, dp, dplen, pc);
> + }
> + break;
> +
> + case BOOTREPLY:
> + /* Remove relay agent info on offer. */
> + if ((dplen = relayagentinfo_remove(dp, length)) == -1)
> + return;
> +
> + /*
> + * VMware PXE "ROMs" confuse the DHCP gateway address
> + * with the IP gateway address. This is a problem if your
> + * DHCP relay is running on something that's not your
> + * network gateway.
> + *
> + * It is purely informational from the relay to the client
> + * so we can safely clear it.
> + */
> + dp->giaddr.s_addr = 0x0;
> +
> + if (ip != interfaces) {
> + debug("forwarded BOOTREPLY for %s to %s",
> + print_hw_addr(pc->pc_htype, pc->pc_hlen,
> + pc->pc_dmac), interfaces->name);
> + send_packet(interfaces, dp, dplen, pc);
> + }
> + break;
> +
> + default:
> + debug("invalid operation type '%d'", dp->op);
> + return;
> + }
> }
> Index: dispatch.c
> ===================================================================
> RCS file: /home/obsdcvs/src/usr.sbin/dhcrelay/dispatch.c,v
> retrieving revision 1.14
> diff -u -p -r1.14 dispatch.c
> --- dispatch.c 8 Dec 2016 19:18:15 -0000 1.14
> +++ dispatch.c 9 Dec 2016 09:03:44 -0000
> @@ -74,7 +74,8 @@ void (*bootp_packet_handler)(struct inte
> static int interface_status(struct interface_info *ifinfo);
>
> struct interface_info *
> -get_interface(const char *ifname, void (*handler)(struct protocol *))
> +get_interface(const char *ifname, void (*handler)(struct protocol *),
> + int isserver)
> {
> struct interface_info *iface;
> struct ifaddrs *ifap, *ifa;
> @@ -145,7 +146,7 @@ get_interface(const char *ifname, void (
> error("interface name '%s' too long", ifname);
>
> /* Register the interface... */
> - if_register_receive(iface);
> + if_register_receive(iface, isserver);
> if_register_send(iface);
> add_protocol(iface->name, iface->rfdesc, handler, iface);
>
>
--