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);
>  
> 

-- 

Reply via email to