diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/dhcrelay/bpf.c | 57 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcp.h | 16 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcpd.h | 6 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcrelay.8 | 22 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcrelay.c | 147 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dispatch.c | 11 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/packet.c | 25 |
7 files changed, 261 insertions, 23 deletions
diff --git a/usr.sbin/dhcrelay/bpf.c b/usr.sbin/dhcrelay/bpf.c index a442b5978d2..51bd2dd9e16 100644 --- a/usr.sbin/dhcrelay/bpf.c +++ b/usr.sbin/dhcrelay/bpf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bpf.c,v 1.6 2008/09/15 20:39:21 claudio Exp $ */ +/* $OpenBSD: bpf.c,v 1.7 2009/09/03 11:56:49 reyk Exp $ */ /* BPF socket interface code, originally contributed by Archie Cobbs. */ @@ -45,6 +45,8 @@ #include <sys/uio.h> #include <net/bpf.h> +#include <net/if_types.h> + #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/udp.h> @@ -126,6 +128,41 @@ struct bpf_insn dhcp_bpf_filter[] = { int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn); +/* + * Packet filter program: encapsulated 'ip and udp and dst port SERVER_PORT' + */ +struct bpf_insn dhcp_bpf_efilter[] = { + /* Make sure this is an encapsulated AF_INET packet... */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, AF_INET << 24, 0, 10), + + /* Make sure it's an IPIP packet... */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 21), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_IPIP, 0, 8), + + /* Make sure it's an encapsulated UDP packet... */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 41), + 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, 38), + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), + + /* Get the IP header length... */ + BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 32), + + /* Make sure it's to the right port... */ + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 34), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SERVER_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_efilter_len = sizeof(dhcp_bpf_efilter) / sizeof(struct bpf_insn); /* * Packet write filter program: 'ip and udp and src port SERVER_PORT' @@ -201,9 +238,13 @@ if_register_receive(struct interface_info *info) info->rbuf_len = 0; /* Set up the bpf filter program structure. */ - p.bf_len = dhcp_bpf_filter_len; - p.bf_insns = dhcp_bpf_filter; - + if (info->hw_address.htype == HTYPE_IPSEC_TUNNEL) { + p.bf_len = dhcp_bpf_efilter_len; + p.bf_insns = dhcp_bpf_efilter; + } else { + p.bf_len = dhcp_bpf_filter_len; + p.bf_insns = dhcp_bpf_filter; + } if (ioctl(info->rfdesc, BIOCSETF, &p) == -1) error("Can't install packet filter program: %m"); @@ -228,6 +269,13 @@ send_packet(struct interface_info *interface, struct iovec iov[2]; int result, bufp = 0; + if (interface->hw_address.htype == HTYPE_IPSEC_TUNNEL) { + socklen_t slen = sizeof(*to); + result = sendto(server_fd, raw, len, 0, + (struct sockaddr *)to, slen); + goto done; + } + /* Assemble the headers... */ assemble_hw_header(interface, buf, &bufp, hto); assemble_udp_ip_header(interface, buf, &bufp, from.s_addr, @@ -240,6 +288,7 @@ send_packet(struct interface_info *interface, iov[1].iov_len = len; result = writev(interface->wfdesc, iov, 2); + done: if (result == -1) warning("send_packet: %m"); return (result); diff --git a/usr.sbin/dhcrelay/dhcp.h b/usr.sbin/dhcrelay/dhcp.h index 50281ea6f92..67feae846a2 100644 --- a/usr.sbin/dhcrelay/dhcp.h +++ b/usr.sbin/dhcrelay/dhcp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcp.h,v 1.4 2007/03/02 18:26:29 stevesk Exp $ */ +/* $OpenBSD: dhcp.h,v 1.5 2009/09/03 11:56:49 reyk Exp $ */ /* Protocol structures... */ @@ -80,13 +80,15 @@ struct dhcp_packet { #define BOOTP_BROADCAST 32768L /* Possible values for hardware type (htype) field... */ -#define HTYPE_ETHER 1 /* Ethernet */ -#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */ -#define HTYPE_FDDI 8 /* FDDI... */ +#define HTYPE_ETHER 1 /* Ethernet */ +#define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */ +#define HTYPE_FDDI 8 /* FDDI... */ +#define HTYPE_IPSEC_TUNNEL 31 /* IPsec Tunnel (RFC3456) */ /* Magic cookie validating dhcp options field (and bootp vendor extensions field). */ #define DHCP_OPTIONS_COOKIE "\143\202\123\143" +#define DHCP_OPTIONS_COOKIE_LEN 4 /* DHCP Option codes: */ @@ -153,6 +155,7 @@ struct dhcp_packet { #define DHO_DHCP_CLASS_IDENTIFIER 60 #define DHO_DHCP_CLIENT_IDENTIFIER 61 #define DHO_DHCP_USER_CLASS_ID 77 +#define DHO_RELAY_AGENT_INFORMATION 82 #define DHO_END 255 /* DHCP message types. */ @@ -164,3 +167,8 @@ struct dhcp_packet { #define DHCPNAK 6 #define DHCPRELEASE 7 #define DHCPINFORM 8 + +/* Relay Agent Information sub-options */ +#define RAI_CIRCUIT_ID 1 +#define RAI_REMOTE_ID 2 +#define RAI_AGENT_ID 3 diff --git a/usr.sbin/dhcrelay/dhcpd.h b/usr.sbin/dhcrelay/dhcpd.h index 332dfb33a14..c1d6917460b 100644 --- a/usr.sbin/dhcrelay/dhcpd.h +++ b/usr.sbin/dhcrelay/dhcpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcpd.h,v 1.11 2007/01/04 19:12:41 stevesk Exp $ */ +/* $OpenBSD: dhcpd.h,v 1.12 2009/09/03 11:56:49 reyk Exp $ */ /* * Copyright (c) 2004 Henning Brauer <henning@openbsd.org> @@ -150,7 +150,8 @@ ssize_t receive_packet(struct interface_info *, unsigned char *, size_t, /* dispatch.c */ extern void (*bootp_packet_handler)(struct interface_info *, - struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *); + struct dhcp_packet *, int, unsigned int, struct iaddr, + struct hardware *); void discover_interfaces(struct interface_info *); void dispatch(void); void got_one(struct protocol *); @@ -170,6 +171,7 @@ ssize_t decode_udp_ip_header(struct interface_info *, unsigned char *, /* dhcrelay.c */ extern u_int16_t server_port; extern u_int16_t client_port; +extern int server_fd; /* crap */ extern time_t cur_time; diff --git a/usr.sbin/dhcrelay/dhcrelay.8 b/usr.sbin/dhcrelay/dhcrelay.8 index ab5794391b1..07893c00432 100644 --- a/usr.sbin/dhcrelay/dhcrelay.8 +++ b/usr.sbin/dhcrelay/dhcrelay.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: dhcrelay.8,v 1.8 2007/05/31 19:20:23 jmc Exp $ +.\" $OpenBSD: dhcrelay.8,v 1.9 2009/09/03 11:56:49 reyk Exp $ .\" .\" Copyright (c) 1997 The Internet Software Consortium. .\" All rights reserved. @@ -36,7 +36,7 @@ .\" see ``http://www.isc.org/isc''. To learn more about Vixie .\" Enterprises, see ``http://www.vix.com''. .\" -.Dd $Mdocdate: May 31 2007 $ +.Dd $Mdocdate: September 3 2009 $ .Dt DHCRELAY 8 .Os .Sh NAME @@ -44,7 +44,7 @@ .Nd Dynamic Host Configuration Protocol Relay Agent .Sh SYNOPSIS .Nm -.Op Fl d +.Op Fl do .Fl i Ar interface .Ar server1 Op Ar ... serverN .Sh DESCRIPTION @@ -69,6 +69,14 @@ as well as the name of the network interface that should attempt to configure, must be specified on the command line. .Pp +.Nm +supports relaying of DHCP traffic to configure IPsec tunnel mode +clients when listening on the +.Xr enc 4 +interface. +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 The options are as follows: .Bl -tag -width Ds .It Fl d @@ -82,12 +90,18 @@ to always run as a foreground process. The name of the network interface that .Nm should attempt to configure. +At least one IPv4 address has to be configured on this interface. +.It Fl o +Add the relay agent information option. +By default, this is only enabled for the +.Xr enc 4 +interface. .El .Sh SEE ALSO .Xr dhclient 8 , .Xr dhcpd 8 .Pp -RFC 2132, RFC 2131. +RFC 2132, RFC 2131, RFC 3046, RFC 3456 .Sh AUTHORS .An -nosplit .Nm diff --git a/usr.sbin/dhcrelay/dhcrelay.c b/usr.sbin/dhcrelay/dhcrelay.c index 1f5f872b998..b80a28494b9 100644 --- a/usr.sbin/dhcrelay/dhcrelay.c +++ b/usr.sbin/dhcrelay/dhcrelay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcrelay.c,v 1.31 2008/07/09 20:08:13 sobrado Exp $ */ +/* $OpenBSD: dhcrelay.c,v 1.32 2009/09/03 11:56:49 reyk Exp $ */ /* * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> @@ -47,6 +47,9 @@ void relay(struct interface_info *, struct dhcp_packet *, int, char *print_hw_addr(int, int, unsigned char *); void got_response(struct protocol *); +ssize_t relay_agentinfo(struct interface_info *, struct dhcp_packet *, + size_t, struct in_addr *, struct in_addr *); + time_t cur_time; int log_perror = 1; @@ -55,6 +58,8 @@ u_int16_t server_port; u_int16_t client_port; int log_priority; struct interface_info *interfaces = NULL; +int server_fd; +int oflag; struct server_list { struct server_list *next; @@ -75,7 +80,7 @@ main(int argc, char *argv[]) openlog(__progname, LOG_NDELAY, DHCPD_LOG_FACILITY); setlogmask(LOG_UPTO(LOG_INFO)); - while ((ch = getopt(argc, argv, "di:")) != -1) { + while ((ch = getopt(argc, argv, "adi:o")) != -1) { switch (ch) { case 'd': no_daemon = 1; @@ -89,6 +94,11 @@ main(int argc, char *argv[]) strlcpy(interfaces->name, optarg, sizeof(interfaces->name)); break; + case 'o': + /* add the relay agent information option */ + oflag++; + break; + default: usage(); /* not reached */ @@ -125,7 +135,8 @@ main(int argc, char *argv[]) argv++; } - log_perror = 0; + if (!no_daemon) + log_perror = 0; if (interfaces == NULL) error("no interface given"); @@ -140,6 +151,10 @@ main(int argc, char *argv[]) discover_interfaces(interfaces); + /* Enable the relay agent option by default for enc0 */ + if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) + oflag++; + bzero(&laddr, sizeof laddr); laddr.sin_len = sizeof laddr; laddr.sin_family = AF_INET; @@ -165,6 +180,21 @@ main(int argc, char *argv[]) add_protocol("server", sp->fd, got_response, sp); } + /* Socket used to forward packets to the DHCP server */ + if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) { + laddr.sin_addr.s_addr = INADDR_ANY; + server_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (server_fd == -1) + error("socket: %m"); + opt = 1; + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, + &opt, sizeof(opt)) == -1) + error("setsockopt: %m"); + if (bind(server_fd, (struct sockaddr *)&laddr, + sizeof(laddr)) == -1) + error("bind: %m"); + } + tzset(); time(&cur_time); @@ -222,6 +252,13 @@ relay(struct interface_info *ip, struct dhcp_packet *packet, int length, memcpy(hto.haddr, packet->chaddr, hto.hlen); hto.htype = packet->htype; + if ((length = relay_agentinfo(interfaces, + packet, length, NULL, &to.sin_addr)) == -1) { + note("ingnoring BOOTREPLY with invalid " + "relay agent information"); + return; + } + if (send_packet(interfaces, packet, length, interfaces->primary_address, &to, &hto) != -1) debug("forwarded BOOTREPLY for %s to %s", @@ -248,6 +285,13 @@ relay(struct interface_info *ip, struct dhcp_packet *packet, int length, correct net. */ packet->giaddr = ip->primary_address; + if ((length = relay_agentinfo(ip, packet, length, + (struct in_addr *)from.iabuf, NULL)) == -1) { + note("ingnoring BOOTREQUEST with invalid " + "relay agent information"); + return; + } + /* Otherwise, it's a BOOTREQUEST, so forward it to all the servers. */ for (sp = servers; sp; sp = sp->next) { @@ -265,7 +309,7 @@ usage(void) { extern char *__progname; - fprintf(stderr, "usage: %s [-d] -i interface server1 [... serverN]\n", + fprintf(stderr, "usage: %s [-do] -i interface server1 [... serverN]\n", __progname); exit(1); } @@ -311,6 +355,7 @@ got_response(struct protocol *l) } u; struct server_list *sp = l->local; + memset(&u, DHO_END, sizeof(u)); if ((result = recv(l->fd, u.packbuf, sizeof(u), 0)) == -1 && errno != ECONNREFUSED) { /* @@ -340,3 +385,97 @@ got_response(struct protocol *l) sp->to.sin_port, ifrom, NULL); } } + +ssize_t +relay_agentinfo(struct interface_info *info, struct dhcp_packet *packet, + size_t length, struct in_addr *from, struct in_addr *to) +{ + u_int8_t *p; + u_int i, j, railen; + ssize_t optlen, maxlen, grow; + + if (!oflag) + return (length); + + /* Buffer length vs. received packet length */ + maxlen = DHCP_MTU_MAX - DHCP_FIXED_LEN - DHCP_OPTIONS_COOKIE_LEN - 1; + optlen = length - DHCP_FIXED_NON_UDP - DHCP_OPTIONS_COOKIE_LEN; + if (maxlen < 1 || optlen < 1) + return (length); + + if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, + DHCP_OPTIONS_COOKIE_LEN) != 0) + return (length); + p = packet->options + DHCP_OPTIONS_COOKIE_LEN; + + for (i = 0; i < (u_int)optlen && *p != DHO_END;) { + if (*p == DHO_PAD) + j = 1; + else + j = p[1] + 2; + + if ((i + j) > (u_int)optlen) { + warning("truncated dhcp options"); + break; + } + + /* Revert any other relay agent information */ + if (*p == DHO_RELAY_AGENT_INFORMATION) { + if (to != NULL) { + /* Check the relay agent information */ + railen = 8 + sizeof(struct in_addr); + if (j >= railen && + p[1] == (railen - 2) && + p[2] == RAI_CIRCUIT_ID && + p[3] == 2 && + p[4] == (u_int8_t)(info->index << 8) && + p[5] == (info->index & 0xff) && + p[6] == RAI_REMOTE_ID && + p[7] == sizeof(*to)) + memcpy(to, p + 8, sizeof(*to)); + + /* It should be the last option */ + memset(p, 0, j); + *p = DHO_END; + } else { + /* Discard invalid option from a client */ + if (!packet->giaddr.s_addr) + return (-1); + } + return (length); + } + + p += j; + i += j; + + if (from != NULL && (*p == DHO_END || (i >= optlen))) { + j = 8 + sizeof(*from); + if ((i + j) > (u_int)maxlen) { + warning("skipping agent information"); + break; + } + + /* Append the relay agent information if it fits */ + p[0] = DHO_RELAY_AGENT_INFORMATION; + p[1] = j - 2; + p[2] = RAI_CIRCUIT_ID; + p[3] = 2; + p[4] = info->index << 8; + p[5] = info->index & 0xff; + p[6] = RAI_REMOTE_ID; + p[7] = sizeof(*from); + memcpy(p + 8, from, sizeof(*from)); + + /* Do we need to increase the packet length? */ + grow = j + 1 - (optlen - i); + if (grow > 0) + length += grow; + p += j; + + *p = DHO_END; + break; + } + } + + return (length); +} diff --git a/usr.sbin/dhcrelay/dispatch.c b/usr.sbin/dhcrelay/dispatch.c index cec8edd4760..19155f9b6a8 100644 --- a/usr.sbin/dhcrelay/dispatch.c +++ b/usr.sbin/dhcrelay/dispatch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dispatch.c,v 1.8 2008/09/15 20:39:21 claudio Exp $ */ +/* $OpenBSD: dispatch.c,v 1.9 2009/09/03 11:56:49 reyk Exp $ */ /* * Copyright 2004 Henning Brauer <henning@openbsd.org> @@ -44,6 +44,8 @@ #include <sys/ioctl.h> #include <net/if_media.h> +#include <net/if_types.h> + #include <ifaddrs.h> #include <poll.h> @@ -90,10 +92,15 @@ discover_interfaces(struct interface_info *iface) if (ifa->ifa_addr->sa_family == AF_LINK) { struct sockaddr_dl *foo = (struct sockaddr_dl *)ifa->ifa_addr; + struct if_data *ifi = + (struct if_data *)ifa->ifa_data; iface->index = foo->sdl_index; iface->hw_address.hlen = foo->sdl_alen; - iface->hw_address.htype = HTYPE_ETHER; /* XXX */ + if (ifi->ifi_type == IFT_ENC) + iface->hw_address.htype = HTYPE_IPSEC_TUNNEL; + else + iface->hw_address.htype = HTYPE_ETHER; /* XXX */ memcpy(iface->hw_address.haddr, LLADDR(foo), foo->sdl_alen); } else if (ifa->ifa_addr->sa_family == AF_INET) { diff --git a/usr.sbin/dhcrelay/packet.c b/usr.sbin/dhcrelay/packet.c index 7ba4acb6649..3c1a62f6b6f 100644 --- a/usr.sbin/dhcrelay/packet.c +++ b/usr.sbin/dhcrelay/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.2 2004/04/20 20:56:47 canacar Exp $ */ +/* $OpenBSD: packet.c,v 1.3 2009/09/03 11:56:49 reyk Exp $ */ /* Packet assembly code, originally contributed by Archie Cobbs. */ @@ -42,6 +42,7 @@ #include "dhcpd.h" +#include <net/if_enc.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/udp.h> @@ -148,14 +149,32 @@ decode_hw_header(struct interface_info *interface, unsigned char *buf, int bufix, struct hardware *from) { struct ether_header eh; + size_t offset = 0; - memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE); + if (interface->hw_address.htype == HTYPE_IPSEC_TUNNEL) { + u_int32_t ip_len; + struct ip *ip; + + bufix += ENC_HDRLEN; + ip_len = (buf[bufix] & 0xf) << 2; + ip = (struct ip *)(buf + bufix); + + /* Encapsulated IP */ + if (ip->ip_p != IPPROTO_IPIP) + return (-1); + + bzero(&eh, sizeof(eh)); + offset = ENC_HDRLEN + ip_len; + } else { + memcpy(&eh, buf + bufix, ETHER_HEADER_SIZE); + offset = sizeof(eh); + } memcpy(from->haddr, eh.ether_shost, sizeof(eh.ether_shost)); from->htype = ARPHRD_ETHER; from->hlen = sizeof(eh.ether_shost); - return (sizeof(eh)); + return (offset); } ssize_t |