I'm implementing some features for dhcrelay and to make them fit I need
some clean ups in the dhcrelay(8) first. This diff changes most of the
input/output functions prototypes to take one parameter with all addresses
instead of passing multiple parameters.

Basically this will make input functions gather more information (source/
destination MACs, source/destination IPs, source/destination ports) and
use it in the output instead of trying to figure out this information along
the way.

With this we will be able to add IPv6 support and layer 2 relaying.

-------
Note:
While testing this I noticed that even though the server socket is sending
the wrong source port, the dhcp server doesn't care about it and it works.
But this can be easily fixed by changing this line in dhcrelay.c:
...
main() {
...
        laddr.sin_port = server_port;
...

to
        laddr.sin_port = client_port;

I'll fix this in another diff.
------

ok?

Index: bpf.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/bpf.c,v
retrieving revision 1.11
diff -u -p -r1.11 bpf.c
--- bpf.c       28 May 2016 07:00:18 -0000      1.11
+++ bpf.c       7 Dec 2016 13:44:35 -0000
@@ -258,24 +258,23 @@ if_register_receive(struct interface_inf
 
 ssize_t
 send_packet(struct interface_info *interface,
-    struct dhcp_packet *raw, size_t len, struct in_addr from,
-    struct sockaddr_in *to, struct hardware *hto)
+    struct dhcp_packet *raw, size_t len, struct packet_ctx *pc)
 {
        unsigned char buf[256];
        struct iovec iov[2];
        int result, bufp = 0;
 
        if (interface->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
-               socklen_t slen = sizeof(*to);
+               socklen_t slen = pc->pc_dss.ss_len;
                result = sendto(server_fd, raw, len, 0,
-                   (struct sockaddr *)to, slen);
+                   (struct sockaddr *)&pc->pc_dss, slen);
                goto done;
        }
 
        /* Assemble the headers... */
-       assemble_hw_header(interface, buf, &bufp, hto);
-       assemble_udp_ip_header(interface, buf, &bufp, from.s_addr,
-           to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
+       assemble_hw_header(interface, buf, &bufp, pc);
+       assemble_udp_ip_header(interface, buf, &bufp, pc,
+           (unsigned char *)raw, len);
 
        /* Fire it off */
        iov[0].iov_base = (char *)buf;
@@ -292,7 +291,7 @@ send_packet(struct interface_info *inter
 
 ssize_t
 receive_packet(struct interface_info *interface, unsigned char *buf,
-    size_t len, struct sockaddr_in *from, struct hardware *hfrom)
+    size_t len, struct packet_ctx *pc)
 {
        int length = 0, offset = 0;
        struct bpf_hdr hdr;
@@ -358,7 +357,7 @@ receive_packet(struct interface_info *in
 
                /* Decode the physical header... */
                offset = decode_hw_header(interface,
-                   interface->rbuf, interface->rbuf_offset, hfrom);
+                   interface->rbuf, interface->rbuf_offset, pc);
 
                /*
                 * If a physical layer checksum failed (dunno of any
@@ -374,7 +373,7 @@ receive_packet(struct interface_info *in
 
                /* Decode the IP and UDP headers... */
                offset = decode_udp_ip_header(interface, interface->rbuf,
-                   interface->rbuf_offset, from, hdr.bh_caplen);
+                   interface->rbuf_offset, pc, hdr.bh_caplen);
 
                /* If the IP or UDP checksum was bad, skip the packet... */
                if (offset < 0) {
Index: dhcpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/dhcpd.h,v
retrieving revision 1.15
diff -u -p -r1.15 dhcpd.h
--- dhcpd.h     7 Dec 2016 13:19:18 -0000       1.15
+++ dhcpd.h     7 Dec 2016 13:44:35 -0000
@@ -42,15 +42,28 @@
 #define        SERVER_PORT     67
 #define        CLIENT_PORT     68
 
+/* Maximum size of client hardware address. */
+#define CHADDR_SIZE    16
+
+struct packet_ctx {
+       uint8_t                          pc_htype;
+       uint8_t                          pc_hlen;
+       uint8_t                          pc_smac[CHADDR_SIZE];
+       uint8_t                          pc_dmac[CHADDR_SIZE];
+
+       struct sockaddr_storage          pc_sss;
+       struct sockaddr_storage          pc_dss;
+};
+
 struct iaddr {
        int len;
-       unsigned char iabuf[16];
+       unsigned char iabuf[CHADDR_SIZE];
 };
 
 struct hardware {
        u_int8_t htype;
        u_int8_t hlen;
-       u_int8_t haddr[16];
+       u_int8_t haddr[CHADDR_SIZE];
 };
 
 /* Possible states in which the client can be. */
@@ -112,15 +125,13 @@ int if_register_bpf(struct interface_inf
 void if_register_send(struct interface_info *);
 void if_register_receive(struct interface_info *);
 ssize_t send_packet(struct interface_info *,
-    struct dhcp_packet *, size_t, struct in_addr,
-    struct sockaddr_in *, struct hardware *);
+    struct dhcp_packet *, size_t, struct packet_ctx *);
 ssize_t receive_packet(struct interface_info *, unsigned char *, size_t,
-    struct sockaddr_in *, struct hardware *);
+    struct packet_ctx *);
 
 /* dispatch.c */
 extern void (*bootp_packet_handler)(struct interface_info *,
-    struct dhcp_packet *, int, unsigned int, struct iaddr,
-    struct hardware *);
+    struct dhcp_packet *, int, struct packet_ctx *);
 struct interface_info *get_interface(const char *,
     void (*)(struct protocol *));
 void dispatch(void);
@@ -129,14 +140,15 @@ void add_protocol(char *, int, void (*)(
 void remove_protocol(struct protocol *);
 
 /* packet.c */
+struct sockaddr_in *ss2sin(struct sockaddr_storage *);
 void assemble_hw_header(struct interface_info *, unsigned char *,
-    int *, struct hardware *);
+    int *, struct packet_ctx *);
 void assemble_udp_ip_header(struct interface_info *, unsigned char *,
-    int *, u_int32_t, u_int32_t, unsigned int, unsigned char *, int);
+    int *, struct packet_ctx *pc, unsigned char *, int);
 ssize_t decode_hw_header(struct interface_info *, unsigned char *,
-    int, struct hardware *);
+    int, struct packet_ctx *);
 ssize_t decode_udp_ip_header(struct interface_info *, unsigned char *,
-    int, struct sockaddr_in *, int);
+    int, struct packet_ctx *, int);
 
 /* dhcrelay.c */
 extern u_int16_t server_port;
Index: dhcrelay.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/dhcrelay.c,v
retrieving revision 1.44
diff -u -p -r1.44 dhcrelay.c
--- dhcrelay.c  7 Dec 2016 13:19:18 -0000       1.44
+++ dhcrelay.c  7 Dec 2016 13:44:35 -0000
@@ -47,6 +47,8 @@
 
 #include <net/if.h>
 
+#include <netinet/if_ether.h>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <netdb.h>
@@ -65,7 +67,7 @@
 void    usage(void);
 int     rdaemon(int);
 void    relay(struct interface_info *, struct dhcp_packet *, int,
-           unsigned int, struct iaddr, struct hardware *);
+           struct packet_ctx *);
 char   *print_hw_addr(int, int, unsigned char *);
 void    got_response(struct protocol *);
 int     get_rdomain(char *);
@@ -256,11 +258,10 @@ main(int argc, char *argv[])
 
 void
 relay(struct interface_info *ip, struct dhcp_packet *packet, int length,
-    unsigned int from_port, struct iaddr from, struct hardware *hfrom)
+    struct packet_ctx *pc)
 {
        struct server_list      *sp;
        struct sockaddr_in       to;
-       struct hardware          hto;
 
        if (packet->hlen > sizeof packet->chaddr) {
                note("Discarding packet with invalid hlen.");
@@ -279,13 +280,14 @@ relay(struct interface_info *ip, struct 
                }
                to.sin_family = AF_INET;
                to.sin_len = sizeof to;
+               memcpy(&ss2sin(&pc->pc_dss)->sin_addr, &to.sin_addr,
+                   sizeof(ss2sin(&pc->pc_dss)->sin_addr));
 
-               /* Set up the hardware destination address. */
-               hto.hlen = packet->hlen;
-               if (hto.hlen > sizeof hto.haddr)
-                       hto.hlen = sizeof hto.haddr;
-               memcpy(hto.haddr, packet->chaddr, hto.hlen);
-               hto.htype = packet->htype;
+               pc->pc_hlen = packet->hlen;
+               if (pc->pc_hlen > CHADDR_SIZE)
+                       pc->pc_hlen = CHADDR_SIZE;
+               memcpy(pc->pc_dmac, packet->chaddr, pc->pc_hlen);
+               pc->pc_htype = packet->htype;
 
                if ((length = relay_agentinfo(interfaces,
                    packet, length, NULL, &to.sin_addr)) == -1) {
@@ -305,8 +307,7 @@ relay(struct interface_info *ip, struct 
                 */
                packet->giaddr.s_addr = 0x0;
 
-               if (send_packet(interfaces, packet, length,
-                   interfaces->primary_address, &to, &hto) != -1)
+               if (send_packet(interfaces, packet, length, pc) != -1)
                        debug("forwarded BOOTREPLY for %s to %s",
                            print_hw_addr(packet->htype, packet->hlen,
                            packet->chaddr), inet_ntoa(to.sin_addr));
@@ -332,7 +333,7 @@ relay(struct interface_info *ip, struct 
        packet->giaddr = ip->primary_address;
 
        if ((length = relay_agentinfo(ip, packet, length,
-           (struct in_addr *)from.iabuf, NULL)) == -1) {
+           &ss2sin(&pc->pc_sss)->sin_addr, NULL)) == -1) {
                note("ignoring BOOTREQUEST with invalid "
                    "relay agent information");
                return;
@@ -420,8 +421,8 @@ bad:
 void
 got_response(struct protocol *l)
 {
+       struct packet_ctx pc;
        ssize_t result;
-       struct iaddr ifrom;
        union {
                /*
                 * Packet input buffer.  Must be as large as largest
@@ -454,13 +455,19 @@ got_response(struct protocol *l)
                return;
        }
 
-       if (bootp_packet_handler) {
-               ifrom.len = 4;
-               memcpy(ifrom.iabuf, &sp->to.sin_addr, ifrom.len);
+       memset(&pc, 0, sizeof(pc));
+       pc.pc_sss.ss_family = AF_INET;
+       pc.pc_sss.ss_len = sizeof(struct sockaddr_in);
+       memcpy(&ss2sin(&pc.pc_sss)->sin_addr, &sp->to.sin_addr,
+           sizeof(ss2sin(&pc.pc_sss)->sin_addr));
+       ss2sin(&pc.pc_sss)->sin_port = server_port;
+
+       pc.pc_dss.ss_family = AF_INET;
+       pc.pc_dss.ss_len = sizeof(struct sockaddr_in);
+       ss2sin(&pc.pc_dss)->sin_port = client_port;
 
-               (*bootp_packet_handler)(NULL, &u.packet, result,
-                   sp->to.sin_port, ifrom, NULL);
-       }
+       if (bootp_packet_handler)
+               (*bootp_packet_handler)(NULL, &u.packet, result, &pc);
 }
 
 ssize_t
Index: dispatch.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/dispatch.c,v
retrieving revision 1.12
diff -u -p -r1.12 dispatch.c
--- dispatch.c  7 Dec 2016 13:19:18 -0000       1.12
+++ dispatch.c  7 Dec 2016 13:44:35 -0000
@@ -49,6 +49,7 @@
 #include <net/if_types.h>
 
 #include <netinet/in.h>
+#include <netinet/if_ether.h>
 
 #include <errno.h>
 #include <ifaddrs.h>
@@ -69,8 +70,7 @@ static struct timeout *free_timeouts;
 static int interfaces_invalidated;
 
 void (*bootp_packet_handler)(struct interface_info *,
-    struct dhcp_packet *, int, unsigned int,
-    struct iaddr, struct hardware *);
+    struct dhcp_packet *, int, struct packet_ctx *);
 
 static int interface_status(struct interface_info *ifinfo);
 
@@ -261,9 +261,7 @@ another:
 void
 got_one(struct protocol *l)
 {
-       struct sockaddr_in from;
-       struct hardware hfrom;
-       struct iaddr ifrom;
+       struct packet_ctx pc;
        size_t result;
        union {
                /*
@@ -275,8 +273,9 @@ got_one(struct protocol *l)
        } u;
        struct interface_info *ip = l->local;
 
-       if ((result = receive_packet(ip, u.packbuf, sizeof(u), &from,
-           &hfrom)) == -1) {
+       memset(&pc, 0, sizeof(pc));
+
+       if ((result = receive_packet(ip, u.packbuf, sizeof(u), &pc)) == -1) {
                warning("receive_packet failed on %s: %s", ip->name,
                    strerror(errno));
                ip->errors++;
@@ -296,13 +295,8 @@ got_one(struct protocol *l)
        if (result == 0)
                return;
 
-       if (bootp_packet_handler) {
-               ifrom.len = 4;
-               memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len);
-
-               (*bootp_packet_handler)(ip, &u.packet, result,
-                   from.sin_port, ifrom, &hfrom);
-       }
+       if (bootp_packet_handler)
+               (*bootp_packet_handler)(ip, &u.packet, result, &pc);
 }
 
 int
Index: errwarn.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/errwarn.c,v
retrieving revision 1.6
diff -u -p -r1.6 errwarn.c
--- errwarn.c   7 Feb 2016 00:49:28 -0000       1.6
+++ errwarn.c   7 Dec 2016 13:44:35 -0000
@@ -46,6 +46,7 @@
 #include <net/if.h>
 
 #include <netinet/in.h>
+#include <netinet/if_ether.h>
 
 #include <errno.h>
 #include <stdarg.h>
Index: packet.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/packet.c,v
retrieving revision 1.11
diff -u -p -r1.11 packet.c
--- packet.c    7 Feb 2016 00:49:28 -0000       1.11
+++ packet.c    7 Dec 2016 13:44:35 -0000
@@ -62,6 +62,12 @@
 u_int32_t      checksum(unsigned char *, unsigned, u_int32_t);
 u_int32_t      wrapsum(u_int32_t);
 
+struct sockaddr_in *
+ss2sin(struct sockaddr_storage *ss)
+{
+       return ((struct sockaddr_in *)ss);
+}
+
 u_int32_t
 checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
 {
@@ -97,17 +103,13 @@ wrapsum(u_int32_t sum)
 
 void
 assemble_hw_header(struct interface_info *interface, unsigned char *buf,
-    int *bufix, struct hardware *to)
+    int *bufix, struct packet_ctx *pc)
 {
        struct ether_header eh;
 
-       if (to != NULL && to->hlen == 6) /* XXX */
-               memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
-       else
-               memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
-
-       /* source address is filled in by the kernel */
-       memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost));
+       /* Use the supplied address or let the kernel fill it. */
+       memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN);
+       memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN);
 
        eh.ether_type = htons(ETHERTYPE_IP);
 
@@ -117,8 +119,7 @@ assemble_hw_header(struct interface_info
 
 void
 assemble_udp_ip_header(struct interface_info *interface, unsigned char *buf,
-    int *bufix, u_int32_t from, u_int32_t to, unsigned int port,
-    unsigned char *data, int len)
+    int *bufix, struct packet_ctx *pc, unsigned char *data, int len)
 {
        struct ip ip;
        struct udphdr udp;
@@ -132,15 +133,15 @@ assemble_udp_ip_header(struct interface_
        ip.ip_ttl = 16;
        ip.ip_p = IPPROTO_UDP;
        ip.ip_sum = 0;
-       ip.ip_src.s_addr = from;
-       ip.ip_dst.s_addr = to;
+       ip.ip_src.s_addr = ss2sin(&pc->pc_sss)->sin_addr.s_addr;
+       ip.ip_dst.s_addr = ss2sin(&pc->pc_dss)->sin_addr.s_addr;
 
        ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0));
        memcpy(&buf[*bufix], &ip, sizeof(ip));
        *bufix += sizeof(ip);
 
-       udp.uh_sport = server_port;     /* XXX */
-       udp.uh_dport = port;                    /* XXX */
+       udp.uh_sport = ss2sin(&pc->pc_sss)->sin_port;
+       udp.uh_dport = ss2sin(&pc->pc_dss)->sin_port;
        udp.uh_ulen = htons(sizeof(udp) + len);
        memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
 
@@ -155,9 +156,8 @@ assemble_udp_ip_header(struct interface_
 
 ssize_t
 decode_hw_header(struct interface_info *interface, unsigned char *buf,
-    int bufix, struct hardware *from)
+    int bufix, struct packet_ctx *pc)
 {
-       struct ether_header eh;
        size_t offset = 0;
 
        if (interface->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
@@ -172,23 +172,24 @@ decode_hw_header(struct interface_info *
                if (ip->ip_p != IPPROTO_IPIP)
                        return (-1);
 
-               bzero(&eh, sizeof(eh));
+               memset(pc->pc_dmac, 0xff, ETHER_ADDR_LEN);
                offset = ENC_HDRLEN + ip_len;
-       } else {        
-               memcpy(&eh, buf + bufix, ETHER_HDR_LEN);
-               offset = sizeof(eh);
+       } else {
+               memcpy(pc->pc_dmac, buf + bufix, ETHER_ADDR_LEN);
+               memcpy(pc->pc_smac, buf + bufix + ETHER_ADDR_LEN,
+                   ETHER_ADDR_LEN);
+               offset = sizeof(struct ether_header);
        }
 
-       memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost));
-       from->htype = ARPHRD_ETHER;
-       from->hlen = sizeof(eh.ether_shost);
+       pc->pc_htype = ARPHRD_ETHER;
+       pc->pc_hlen = ETHER_ADDR_LEN;
 
        return (offset);
 }
 
 ssize_t
 decode_udp_ip_header(struct interface_info *interface, unsigned char *buf,
-    int bufix, struct sockaddr_in *from, int buflen)
+    int bufix, struct packet_ctx *pc, int buflen)
 {
        struct ip *ip;
        struct udphdr *udp;
@@ -224,7 +225,15 @@ decode_udp_ip_header(struct interface_in
                return (-1);
        }
 
-       memcpy(&from->sin_addr, &ip->ip_src, sizeof(from->sin_addr));
+       pc->pc_sss.ss_len = sizeof(struct sockaddr_in);
+       pc->pc_sss.ss_family = AF_INET;
+       memcpy(&ss2sin(&pc->pc_sss)->sin_addr, &ip->ip_src,
+           sizeof(ss2sin(&pc->pc_sss)->sin_addr));
+
+       pc->pc_dss.ss_len = sizeof(struct sockaddr_in);
+       pc->pc_dss.ss_family = AF_INET;
+       memcpy(&ss2sin(&pc->pc_dss)->sin_addr, &ip->ip_dst,
+           sizeof(ss2sin(&pc->pc_dss)->sin_addr));
 
 #ifdef DEBUG
        if (ntohs(ip->ip_len) != buflen)
@@ -291,7 +300,8 @@ decode_udp_ip_header(struct interface_in
                return (-1);
        }
 
-       memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
+       ss2sin(&pc->pc_sss)->sin_port = udp->uh_sport;
+       ss2sin(&pc->pc_dss)->sin_port = udp->uh_dport;
 
        return (ip_len + sizeof(*udp));
 }

Reply via email to