From 563f0b8aef9558a900f215dfccc571eab99a0223 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Sat, 23 Dec 2017 01:18:23 +0100 Subject: Merged rt44535 (relay port) --- common/bpf.c | 57 +++++++++++++++++++++++++++++++++++++++++--- common/discover.c | 43 ++++++++++++++++++++++++++++++++-- common/dlpi.c | 4 ++++ common/lpf.c | 19 ++++++++++++++- common/nit.c | 4 ++++ common/packet.c | 11 +++++++++ common/raw.c | 4 ++-- common/socket.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++----- common/tables.c | 16 ++++++++----- common/upf.c | 3 +++ 10 files changed, 211 insertions(+), 20 deletions(-) (limited to 'common') diff --git a/common/bpf.c b/common/bpf.c index dc41f809..16076fea 100644 --- a/common/bpf.c +++ b/common/bpf.c @@ -192,12 +192,51 @@ struct bpf_insn dhcp_bpf_filter [] = { BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ /* If we passed all the tests, ask for the whole packet. */ - BPF_STMT(BPF_RET+BPF_K, (u_int)-1), + BPF_STMT (BPF_RET + BPF_K, (u_int)-1), /* Otherwise, drop it. */ - BPF_STMT(BPF_RET+BPF_K, 0), + BPF_STMT (BPF_RET + BPF_K, 0), }; +#if defined(RELAY_PORT) +/* + * For relay port extension + */ +struct bpf_insn dhcp_bpf_relay_filter [] = { + /* 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, 10), + + /* 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, 8), + + /* 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, 6, 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, 67, 2, 0), /* patch */ + + /* relay can have an alternative port... */ + BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ + + /* 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_relay_filter_len = + sizeof dhcp_bpf_relay_filter / sizeof (struct bpf_insn); +#endif + #if defined (DEC_FDDI) struct bpf_insn *bpf_fddi_filter = NULL; #endif @@ -309,7 +348,19 @@ void if_register_receive (info) /* Patch the server port into the BPF program... XXX changes to filter program may require changes to the insn number(s) used below! XXX */ - dhcp_bpf_filter [8].k = ntohs (local_port); +#if defined(RELAY_PORT) + if (relay_port) { + /* + * If user defined relay UDP port, we need to filter + * also on the user UDP port. + */ + p.bf_len = dhcp_bpf_relay_filter_len; + p.bf_insns = dhcp_bpf_relay_filter; + + dhcp_bpf_relay_filter [10].k = ntohs (relay_port); + } +#endif + p.bf_insns [8].k = ntohs (local_port); if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0) log_fatal ("Can't install packet filter program: %m"); diff --git a/common/discover.c b/common/discover.c index f81d63f7..6ef88529 100644 --- a/common/discover.c +++ b/common/discover.c @@ -44,6 +44,7 @@ int interfaces_invalidated; int quiet_interface_discovery; u_int16_t local_port; u_int16_t remote_port; +u_int16_t relay_port = 0; int dhcpv4_over_dhcpv6 = 0; int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); int (*dhcp_interface_discovery_hook) (struct interface_info *); @@ -581,6 +582,10 @@ discover_interfaces(int state) { int ir; isc_result_t status; int wifcount = 0; +#ifdef RELAY_PORT + int updone = 0; + int downdone = 0; +#endif static int setup_fallback = 0; @@ -946,9 +951,39 @@ discover_interfaces(int state) { switch (local_family) { #ifdef DHCPv6 case AF_INET6: +#ifdef RELAY_PORT +#define UPSTREAM(ifp) \ + ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) +#define DOWNSTREAM(ifp) \ + ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM) + + if (relay_port) { + /* + * The normal IPv6 relay only needs one + * socket as long as we find an interface. + * When user relay port is defined, and we + * have two different UDP ports. One to + * receive from DHCP client with port 547, + * and the other is user defined for sending + * to the server or upstream relay agent. + * Thus we need to register sockets for one + * upstream and one downstream interfaces. + */ + if (updone && UPSTREAM(tmp)) + continue; + if (downdone && DOWNSTREAM(tmp)) + continue; + } +#endif status = omapi_register_io_object((omapi_object_t *)tmp, if_readsocket, 0, got_one_v6, 0, 0); +#ifdef RELAY_PORT + if (UPSTREAM(tmp)) + updone++; + else + downdone++; +#endif break; #endif /* DHCPv6 */ case AF_INET: @@ -970,8 +1005,12 @@ discover_interfaces(int state) { * dynamically adding and removing interfaces, but * we're well beyond that point in terms of mess. */ - if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) && - (local_family == AF_INET6)) + if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY)) + && (local_family == AF_INET6) +#if defined(RELAY_PORT) + && ((relay_port == 0) || (updone && downdone)) +#endif + ) break; #endif } /* for (tmp = interfaces; ... */ diff --git a/common/dlpi.c b/common/dlpi.c index b459dc6d..3990bf12 100644 --- a/common/dlpi.c +++ b/common/dlpi.c @@ -409,6 +409,10 @@ void if_deregister_send (info) XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the NIT program! XXX */ +#if defined(RELAY_PORT) +#error "Relay port is not yet supported for DLPI" +#endif + void if_register_receive (info) struct interface_info *info; { diff --git a/common/lpf.c b/common/lpf.c index 6e6ba014..82a279bb 100644 --- a/common/lpf.c +++ b/common/lpf.c @@ -177,6 +177,11 @@ void if_deregister_send (info) extern struct sock_filter dhcp_bpf_filter []; extern int dhcp_bpf_filter_len; +#if defined(RELAY_PORT) +extern struct sock_filter dhcp_bpf_relay_filter []; +extern int dhcp_bpf_relay_filter_len; +#endif + #if defined (HAVE_TR_SUPPORT) extern struct sock_filter dhcp_bpf_tr_filter []; extern int dhcp_bpf_tr_filter_len; @@ -256,7 +261,19 @@ static void lpf_gen_filter_setup (info) /* Patch the server port into the LPF program... XXX changes to filter program may require changes to the insn number(s) used below! XXX */ - dhcp_bpf_filter [8].k = ntohs ((short)local_port); +#if defined(RELAY_PORT) + if (relay_port) { + /* + * If user defined relay UDP port, we need to filter + * also on the user UDP port. + */ + p.len = dhcp_bpf_relay_filter_len; + p.filter = dhcp_bpf_relay_filter; + + dhcp_bpf_relay_filter [10].k = ntohs (relay_port); + } +#endif + dhcp_bpf_filter [8].k = ntohs (local_port); if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p, sizeof p) < 0) { diff --git a/common/nit.c b/common/nit.c index 1e14ebb7..d822c151 100644 --- a/common/nit.c +++ b/common/nit.c @@ -172,6 +172,10 @@ void if_deregister_send (info) XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the NIT program! XXX */ +#if defined(RELAY_PORT) +#error "Relay port is not yet supported for NIT" +#endif + void if_register_receive (info) struct interface_info *info; { diff --git a/common/packet.c b/common/packet.c index d6907fe0..f3f62c19 100644 --- a/common/packet.c +++ b/common/packet.c @@ -167,6 +167,12 @@ void assemble_udp_ip_header (interface, buf, bufix, /* Fill out the UDP header */ udp.uh_sport = local_port; /* XXX */ udp.uh_dport = port; /* XXX */ +#if defined(RELAY_PORT) + /* Change to relay port defined if sending to server */ + if (relay_port && (port == htons(67))) { + udp.uh_sport = relay_port; + } +#endif udp.uh_ulen = htons(sizeof(udp) + len); memset (&udp.uh_sum, 0, sizeof udp.uh_sum); @@ -296,7 +302,12 @@ decode_udp_ip_header(struct interface_info *interface, return -1; /* Is it to the port we're serving? */ +#if defined(RELAY_PORT) + if ((udp.uh_dport != local_port) && + ((relay_port == 0) || (udp.uh_dport != relay_port))) +#else if (udp.uh_dport != local_port) +#endif return -1; #endif /* USERLAND_FILTER */ diff --git a/common/raw.c b/common/raw.c index 3ad0bfd2..5c88b25e 100644 --- a/common/raw.c +++ b/common/raw.c @@ -55,14 +55,14 @@ void if_register_send (info) /* Set up the address we're going to connect to. */ name.sin_family = AF_INET; - name.sin_port = local_port; + name.sin_port = relay_port ? relay_port : local_port; name.sin_addr.s_addr = htonl (INADDR_BROADCAST); memset (name.sin_zero, 0, sizeof (name.sin_zero)); /* List addresses on which we're listening. */ if (!quiet_interface_discovery) log_info ("Sending on %s, port %d", - piaddr (info -> address), htons (local_port)); + piaddr (info -> address), htons (name.sin_port)); if ((sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) log_fatal ("Can't create dhcp socket: %m"); diff --git a/common/socket.c b/common/socket.c index 8879bc20..483eb9c3 100644 --- a/common/socket.c +++ b/common/socket.c @@ -65,6 +65,10 @@ static int no_global_v6_socket = 0; static unsigned int global_v6_socket_references = 0; static int global_v6_socket = -1; +#if defined(RELAY_PORT) +static unsigned int relay_port_v6_socket_references = 0; +static int relay_port_v6_socket = -1; +#endif static void if_register_multicast(struct interface_info *info); #endif @@ -157,6 +161,11 @@ if_register_socket(struct interface_info *info, int family, addr6 = (struct sockaddr_in6 *)&name; addr6->sin6_family = AF_INET6; addr6->sin6_port = local_port; +#if defined(RELAY_PORT) + if (relay_port && + ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) + addr6->sin6_port = relay_port; +#endif /* A server feature */ if (bind_local_address6) { memcpy(&addr6->sin6_addr, @@ -187,7 +196,7 @@ if_register_socket(struct interface_info *info, int family, default: addr = (struct sockaddr_in *)&name; addr->sin_family = AF_INET; - addr->sin_port = local_port; + addr->sin_port = relay_port ? relay_port : local_port; memcpy(&addr->sin_addr, &local_address, sizeof(addr->sin_addr)); @@ -496,6 +505,10 @@ if_register6(struct interface_info *info, int do_multicast) { log_fatal("Impossible condition at %s:%d", MDL); } +#if defined(RELAY_PORT) + if (!relay_port || + ((info->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)) { +#endif if (global_v6_socket_references == 0) { global_v6_socket = if_register_socket(info, AF_INET6, &req_multi, NULL); @@ -527,6 +540,30 @@ if_register6(struct interface_info *info, int do_multicast) { info->wfdesc = global_v6_socket; global_v6_socket_references++; +#if defined(RELAY_PORT) + } else { + /* + * If relay port is defined, we need to register one + * IPv6 UPD socket to handle upstream server or relay agent + * with a non-547 UDP local port. + */ + if ((relay_port_v6_socket_references == 0) && + ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)) { + relay_port_v6_socket = if_register_socket(info, AF_INET6, + &req_multi, NULL); + if (relay_port_v6_socket < 0) { + log_fatal("Impossible condition at %s:%d", MDL); + } else { + log_info("Bound to relay port *:%d", + (int) ntohs(relay_port)); + } + } + info->rfdesc = relay_port_v6_socket; + info->wfdesc = relay_port_v6_socket; + relay_port_v6_socket_references++; + } +#endif + if (req_multi) if_register_multicast(info); @@ -617,6 +654,16 @@ if_deregister6(struct interface_info *info) { global_v6_socket_references--; info->rfdesc = -1; info->wfdesc = -1; +#if defined(RELAY_PORT) + } else if (relay_port && + (info->rfdesc == relay_port_v6_socket) && + (info->wfdesc == relay_port_v6_socket) && + (relay_port_v6_socket_references > 0)) { + /* Dereference the relay port v6 socket. */ + relay_port_v6_socket_references--; + info->rfdesc = -1; + info->wfdesc = -1; +#endif } else { log_fatal("Impossible condition at %s:%d", MDL); } @@ -633,12 +680,23 @@ if_deregister6(struct interface_info *info) { } } - if (!no_global_v6_socket && - (global_v6_socket_references == 0)) { - close(global_v6_socket); - global_v6_socket = -1; + if (!no_global_v6_socket) { + if (global_v6_socket_references == 0) { + close(global_v6_socket); + global_v6_socket = -1; - log_info("Unbound from *:%d", ntohs(local_port)); + log_info("Unbound from *:%d", + (int) ntohs(local_port)); + } +#if defined(RELAY_PORT) + if (relay_port && (relay_port_v6_socket_references == 0)) { + close(relay_port_v6_socket); + relay_port_v6_socket = -1; + + log_info("Unbound from relay port *:%d", + (int) ntohs(relay_port)); + } +#endif } } #endif /* DHCPv6 */ diff --git a/common/tables.c b/common/tables.c index 2350e803..b3d5ae66 100644 --- a/common/tables.c +++ b/common/tables.c @@ -557,12 +557,6 @@ static struct option dhcpv6_options[] = { { "solmax-rt", "L", &dhcpv6_universe, 82, 1 }, { "inf-max-rt", "L", &dhcpv6_universe, 83, 1 }, #endif -#if defined(RFC7710_OPTIONS) - { "v6-captive-portal", "t", &dhcpv6_universe, 103, 1 }, -#endif -#if defined(RFC6153_OPTIONS) - { "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 }, -#endif /* RFC7341 OPTIONS */ #if defined(RFC7341_OPTIONS) @@ -570,6 +564,16 @@ static struct option dhcpv6_options[] = { { "dhcp4-o-dhcp6-server", "6A", &dhcpv6_universe, 88, 1 }, #endif +#if defined(RFC7710_OPTIONS) + { "v6-captive-portal", "t", &dhcpv6_universe, 103, 1 }, +#endif + + { "relay-source-port", "S", &dhcpv6_universe, 135, 1 }, + +#if defined(RFC6153_OPTIONS) + { "ipv6-address-andsf", "6A", &dhcpv6_universe, 143, 1 }, +#endif + { NULL, NULL, NULL, 0, 0 } }; diff --git a/common/upf.c b/common/upf.c index 1b49301e..9785879a 100644 --- a/common/upf.c +++ b/common/upf.c @@ -156,6 +156,9 @@ void if_deregister_send (info) XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the UPF program! XXX */ +#if defined(RELAY_PORT) +#error "Relay port is not yet supported for UPF" +#endif void if_register_receive (info) struct interface_info *info; -- cgit v1.2.1