xref: /openbsd-src/usr.sbin/dhcrelay6/packet.c (revision 9cbab5830b690836fba8f7e46ae8cef69a6fd5c1)
1*9cbab583Srzalamena /*	$OpenBSD: packet.c,v 1.1 2017/03/17 14:45:16 rzalamena Exp $	*/
2*9cbab583Srzalamena 
3*9cbab583Srzalamena /* Packet assembly code, originally contributed by Archie Cobbs. */
4*9cbab583Srzalamena 
5*9cbab583Srzalamena /*
6*9cbab583Srzalamena  * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
7*9cbab583Srzalamena  * All rights reserved.
8*9cbab583Srzalamena  *
9*9cbab583Srzalamena  * Redistribution and use in source and binary forms, with or without
10*9cbab583Srzalamena  * modification, are permitted provided that the following conditions
11*9cbab583Srzalamena  * are met:
12*9cbab583Srzalamena  *
13*9cbab583Srzalamena  * 1. Redistributions of source code must retain the above copyright
14*9cbab583Srzalamena  *    notice, this list of conditions and the following disclaimer.
15*9cbab583Srzalamena  * 2. Redistributions in binary form must reproduce the above copyright
16*9cbab583Srzalamena  *    notice, this list of conditions and the following disclaimer in the
17*9cbab583Srzalamena  *    documentation and/or other materials provided with the distribution.
18*9cbab583Srzalamena  * 3. Neither the name of The Internet Software Consortium nor the names
19*9cbab583Srzalamena  *    of its contributors may be used to endorse or promote products derived
20*9cbab583Srzalamena  *    from this software without specific prior written permission.
21*9cbab583Srzalamena  *
22*9cbab583Srzalamena  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23*9cbab583Srzalamena  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24*9cbab583Srzalamena  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25*9cbab583Srzalamena  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26*9cbab583Srzalamena  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27*9cbab583Srzalamena  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28*9cbab583Srzalamena  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29*9cbab583Srzalamena  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30*9cbab583Srzalamena  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31*9cbab583Srzalamena  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32*9cbab583Srzalamena  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33*9cbab583Srzalamena  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34*9cbab583Srzalamena  * SUCH DAMAGE.
35*9cbab583Srzalamena  *
36*9cbab583Srzalamena  * This software has been written for the Internet Software Consortium
37*9cbab583Srzalamena  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38*9cbab583Srzalamena  * Enterprises.  To learn more about the Internet Software Consortium,
39*9cbab583Srzalamena  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40*9cbab583Srzalamena  * Enterprises, see ``http://www.vix.com''.
41*9cbab583Srzalamena  */
42*9cbab583Srzalamena 
43*9cbab583Srzalamena #include <sys/types.h>
44*9cbab583Srzalamena #include <sys/socket.h>
45*9cbab583Srzalamena 
46*9cbab583Srzalamena #include <arpa/inet.h>
47*9cbab583Srzalamena 
48*9cbab583Srzalamena #include <net/if.h>
49*9cbab583Srzalamena 
50*9cbab583Srzalamena #include <netinet/in.h>
51*9cbab583Srzalamena #include <netinet/ip.h>
52*9cbab583Srzalamena #include <netinet/ip6.h>
53*9cbab583Srzalamena #include <netinet/udp.h>
54*9cbab583Srzalamena #include <netinet/if_ether.h>
55*9cbab583Srzalamena 
56*9cbab583Srzalamena #include <string.h>
57*9cbab583Srzalamena 
58*9cbab583Srzalamena #include "dhcp.h"
59*9cbab583Srzalamena #include "dhcpd.h"
60*9cbab583Srzalamena #include "log.h"
61*9cbab583Srzalamena 
62*9cbab583Srzalamena 
63*9cbab583Srzalamena u_int32_t	checksum(unsigned char *, unsigned, u_int32_t);
64*9cbab583Srzalamena u_int32_t	wrapsum(u_int32_t);
65*9cbab583Srzalamena 
66*9cbab583Srzalamena u_int32_t
checksum(unsigned char * buf,unsigned nbytes,u_int32_t sum)67*9cbab583Srzalamena checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
68*9cbab583Srzalamena {
69*9cbab583Srzalamena 	unsigned int i;
70*9cbab583Srzalamena 
71*9cbab583Srzalamena 	/* Checksum all the pairs of bytes first... */
72*9cbab583Srzalamena 	for (i = 0; i < (nbytes & ~1U); i += 2) {
73*9cbab583Srzalamena 		sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i)));
74*9cbab583Srzalamena 		if (sum > 0xFFFF)
75*9cbab583Srzalamena 			sum -= 0xFFFF;
76*9cbab583Srzalamena 	}
77*9cbab583Srzalamena 
78*9cbab583Srzalamena 	/*
79*9cbab583Srzalamena 	 * If there's a single byte left over, checksum it, too.
80*9cbab583Srzalamena 	 * Network byte order is big-endian, so the remaining byte is
81*9cbab583Srzalamena 	 * the high byte.
82*9cbab583Srzalamena 	 */
83*9cbab583Srzalamena 	if (i < nbytes) {
84*9cbab583Srzalamena 		sum += buf[i] << 8;
85*9cbab583Srzalamena 		if (sum > 0xFFFF)
86*9cbab583Srzalamena 			sum -= 0xFFFF;
87*9cbab583Srzalamena 	}
88*9cbab583Srzalamena 
89*9cbab583Srzalamena 	return (sum);
90*9cbab583Srzalamena }
91*9cbab583Srzalamena 
92*9cbab583Srzalamena u_int32_t
wrapsum(u_int32_t sum)93*9cbab583Srzalamena wrapsum(u_int32_t sum)
94*9cbab583Srzalamena {
95*9cbab583Srzalamena 	sum = ~sum & 0xFFFF;
96*9cbab583Srzalamena 	return (htons(sum));
97*9cbab583Srzalamena }
98*9cbab583Srzalamena 
99*9cbab583Srzalamena void
assemble_hw_header(unsigned char * buf,int * bufix,struct packet_ctx * pc)100*9cbab583Srzalamena assemble_hw_header(unsigned char *buf, int *bufix, struct packet_ctx *pc)
101*9cbab583Srzalamena {
102*9cbab583Srzalamena 	struct ether_header eh;
103*9cbab583Srzalamena 
104*9cbab583Srzalamena 	memcpy(eh.ether_shost, pc->pc_smac, ETHER_ADDR_LEN);
105*9cbab583Srzalamena 	memcpy(eh.ether_dhost, pc->pc_dmac, ETHER_ADDR_LEN);
106*9cbab583Srzalamena 	eh.ether_type = htons(pc->pc_ethertype);
107*9cbab583Srzalamena 
108*9cbab583Srzalamena 	memcpy(&buf[*bufix], &eh, ETHER_HDR_LEN);
109*9cbab583Srzalamena 	*bufix += ETHER_HDR_LEN;
110*9cbab583Srzalamena }
111*9cbab583Srzalamena 
112*9cbab583Srzalamena void
assemble_udp_ip6_header(unsigned char * p,int * off,struct packet_ctx * pc,unsigned char * payload,int plen)113*9cbab583Srzalamena assemble_udp_ip6_header(unsigned char *p, int *off, struct packet_ctx *pc,
114*9cbab583Srzalamena     unsigned char *payload, int plen)
115*9cbab583Srzalamena {
116*9cbab583Srzalamena 	struct ip6_hdr		 ip6;
117*9cbab583Srzalamena 	struct udphdr		 uh;
118*9cbab583Srzalamena 
119*9cbab583Srzalamena 	memset(&ip6, 0, sizeof(ip6));
120*9cbab583Srzalamena 	ip6.ip6_vfc = IPV6_VERSION;
121*9cbab583Srzalamena 	ip6.ip6_nxt = IPPROTO_UDP;
122*9cbab583Srzalamena 	ip6.ip6_src = ss2sin6(&pc->pc_src)->sin6_addr;
123*9cbab583Srzalamena 	ip6.ip6_dst = ss2sin6(&pc->pc_dst)->sin6_addr;
124*9cbab583Srzalamena 	ip6.ip6_plen = htons(sizeof(uh) + plen);
125*9cbab583Srzalamena 	ip6.ip6_hlim = 64;
126*9cbab583Srzalamena 	memcpy(&p[*off], &ip6, sizeof(ip6));
127*9cbab583Srzalamena 	*off += sizeof(ip6);
128*9cbab583Srzalamena 
129*9cbab583Srzalamena 	memset(&uh, 0, sizeof(uh));
130*9cbab583Srzalamena 	uh.uh_ulen = ip6.ip6_plen;
131*9cbab583Srzalamena 	uh.uh_sport = ss2sin6(&pc->pc_src)->sin6_port;
132*9cbab583Srzalamena 	uh.uh_dport = ss2sin6(&pc->pc_dst)->sin6_port;
133*9cbab583Srzalamena 	uh.uh_sum = wrapsum(
134*9cbab583Srzalamena 	    checksum((unsigned char *)&uh, sizeof(uh),
135*9cbab583Srzalamena 	    checksum(payload, plen,
136*9cbab583Srzalamena 	    checksum((unsigned char *)&ip6.ip6_src, sizeof(ip6.ip6_src),
137*9cbab583Srzalamena 	    checksum((unsigned char *)&ip6.ip6_dst, sizeof(ip6.ip6_dst),
138*9cbab583Srzalamena 	    IPPROTO_UDP + ntohs(ip6.ip6_plen)
139*9cbab583Srzalamena 	    ))))
140*9cbab583Srzalamena 	);
141*9cbab583Srzalamena 	memcpy(&p[*off], &uh, sizeof(uh));
142*9cbab583Srzalamena 	*off += sizeof(uh);
143*9cbab583Srzalamena }
144*9cbab583Srzalamena 
145*9cbab583Srzalamena ssize_t
decode_hw_header(unsigned char * buf,int bufix,struct packet_ctx * pc)146*9cbab583Srzalamena decode_hw_header(unsigned char *buf, int bufix, struct packet_ctx *pc)
147*9cbab583Srzalamena {
148*9cbab583Srzalamena 	struct ether_header *ether;
149*9cbab583Srzalamena 
150*9cbab583Srzalamena 	ether = (struct ether_header *)(buf + bufix);
151*9cbab583Srzalamena 	memcpy(pc->pc_dmac, ether->ether_dhost, ETHER_ADDR_LEN);
152*9cbab583Srzalamena 	memcpy(pc->pc_smac, ether->ether_shost, ETHER_ADDR_LEN);
153*9cbab583Srzalamena 	pc->pc_ethertype = ntohs(ether->ether_type);
154*9cbab583Srzalamena 
155*9cbab583Srzalamena 	pc->pc_htype = ARPHRD_ETHER;
156*9cbab583Srzalamena 	pc->pc_hlen = ETHER_ADDR_LEN;
157*9cbab583Srzalamena 
158*9cbab583Srzalamena 	return sizeof(struct ether_header);
159*9cbab583Srzalamena }
160*9cbab583Srzalamena 
161*9cbab583Srzalamena ssize_t
decode_udp_ip6_header(unsigned char * p,int off,struct packet_ctx * pc,size_t plen)162*9cbab583Srzalamena decode_udp_ip6_header(unsigned char *p, int off, struct packet_ctx *pc,
163*9cbab583Srzalamena    size_t plen)
164*9cbab583Srzalamena {
165*9cbab583Srzalamena 	struct ip6_hdr		*ip6;
166*9cbab583Srzalamena 	struct udphdr		*uh;
167*9cbab583Srzalamena 	struct in6_addr		*asrc, *adst;
168*9cbab583Srzalamena 	size_t			 ptotal, poff = 0;
169*9cbab583Srzalamena 	uint16_t		 ocksum, cksum;
170*9cbab583Srzalamena 
171*9cbab583Srzalamena 	/* Check the IPv6 header. */
172*9cbab583Srzalamena 	if (plen < sizeof(*ip6)) {
173*9cbab583Srzalamena 		log_debug("package too small (%ld)", plen);
174*9cbab583Srzalamena 		return -1;
175*9cbab583Srzalamena 	}
176*9cbab583Srzalamena 
177*9cbab583Srzalamena 	ip6 = (struct ip6_hdr *)(p + off);
178*9cbab583Srzalamena 	if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
179*9cbab583Srzalamena 		log_debug("invalid IPv6 version");
180*9cbab583Srzalamena 		return -1;
181*9cbab583Srzalamena 	}
182*9cbab583Srzalamena 
183*9cbab583Srzalamena 	poff += sizeof(*ip6);
184*9cbab583Srzalamena 
185*9cbab583Srzalamena 	ptotal = ntohs(ip6->ip6_plen);
186*9cbab583Srzalamena 	if (ptotal > plen) {
187*9cbab583Srzalamena 		log_debug("expected %ld bytes, but got %ld", ptotal, plen);
188*9cbab583Srzalamena 		return (-1);
189*9cbab583Srzalamena 	}
190*9cbab583Srzalamena 
191*9cbab583Srzalamena 	pc->pc_src.ss_len = sizeof(struct sockaddr_in6);
192*9cbab583Srzalamena 	pc->pc_src.ss_family = AF_INET6;
193*9cbab583Srzalamena 	asrc = &ss2sin6(&pc->pc_src)->sin6_addr;
194*9cbab583Srzalamena 	memcpy(asrc, &ip6->ip6_src, sizeof(*asrc));
195*9cbab583Srzalamena 
196*9cbab583Srzalamena 	pc->pc_dst.ss_len = sizeof(struct sockaddr_in6);
197*9cbab583Srzalamena 	pc->pc_dst.ss_family = AF_INET6;
198*9cbab583Srzalamena 	adst = &ss2sin6(&pc->pc_dst)->sin6_addr;
199*9cbab583Srzalamena 	memcpy(adst, &ip6->ip6_dst, sizeof(*adst));
200*9cbab583Srzalamena 
201*9cbab583Srzalamena 	/* Deal with the UDP header. */
202*9cbab583Srzalamena 	if (ip6->ip6_nxt != IPPROTO_UDP) {
203*9cbab583Srzalamena 		/* We don't support skipping extensions yet. */
204*9cbab583Srzalamena 		log_debug("expected UDP header, got %#02X", ip6->ip6_nxt);
205*9cbab583Srzalamena 		return -1;
206*9cbab583Srzalamena 	}
207*9cbab583Srzalamena 
208*9cbab583Srzalamena 	uh = (struct udphdr *)((uint8_t *)ip6 + sizeof(*ip6));
209*9cbab583Srzalamena 	ss2sin6(&pc->pc_src)->sin6_port = uh->uh_sport;
210*9cbab583Srzalamena 	ss2sin6(&pc->pc_dst)->sin6_port = uh->uh_dport;
211*9cbab583Srzalamena 	ocksum = uh->uh_sum;
212*9cbab583Srzalamena 	uh->uh_sum = 0;
213*9cbab583Srzalamena 	poff += sizeof(*uh);
214*9cbab583Srzalamena 
215*9cbab583Srzalamena 	/* Validate the packet. */
216*9cbab583Srzalamena 	cksum = wrapsum(
217*9cbab583Srzalamena 	    checksum((unsigned char *)asrc, sizeof(*asrc),
218*9cbab583Srzalamena 	    checksum((unsigned char *)adst, sizeof(*adst),
219*9cbab583Srzalamena 	    checksum((unsigned char *)uh, sizeof(*uh),
220*9cbab583Srzalamena 	    checksum(p + off + poff, ptotal - sizeof(*uh),
221*9cbab583Srzalamena 	    IPPROTO_UDP + ntohs(uh->uh_ulen)))))
222*9cbab583Srzalamena 	);
223*9cbab583Srzalamena 
224*9cbab583Srzalamena 	if (ocksum != cksum) {
225*9cbab583Srzalamena 		log_debug("checksum invalid (%#04x != %#04x)",
226*9cbab583Srzalamena 		    ocksum, cksum);
227*9cbab583Srzalamena 		return -1;
228*9cbab583Srzalamena 	}
229*9cbab583Srzalamena 
230*9cbab583Srzalamena 	return poff;
231*9cbab583Srzalamena }
232