1 /* NHRP packet handling functions
2  * Copyright (c) 2014-2015 Timo Teräs
3  *
4  * This file is free software: you may copy, redistribute and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 
10 #include <netinet/if_ether.h>
11 #include "nhrpd.h"
12 #include "zbuf.h"
13 #include "thread.h"
14 #include "hash.h"
15 
16 #include "nhrp_protocol.h"
17 #include "os.h"
18 
19 struct nhrp_reqid_pool nhrp_packet_reqid;
20 
family2proto(int family)21 static uint16_t family2proto(int family)
22 {
23 	switch (family) {
24 	case AF_INET: return ETH_P_IP;
25 	case AF_INET6: return ETH_P_IPV6;
26 	}
27 	return 0;
28 }
29 
proto2family(uint16_t proto)30 static int proto2family(uint16_t proto)
31 {
32 	switch (proto) {
33 	case ETH_P_IP: return AF_INET;
34 	case ETH_P_IPV6: return AF_INET6;
35 	}
36 	return AF_UNSPEC;
37 }
38 
nhrp_packet_push(struct zbuf * zb,uint8_t type,const union sockunion * src_nbma,const union sockunion * src_proto,const union sockunion * dst_proto)39 struct nhrp_packet_header *nhrp_packet_push(
40 	struct zbuf *zb, uint8_t type,
41 	const union sockunion *src_nbma,
42 	const union sockunion *src_proto,
43 	const union sockunion *dst_proto)
44 {
45 	struct nhrp_packet_header *hdr;
46 
47 	hdr = zbuf_push(zb, struct nhrp_packet_header);
48 	if (!hdr) return NULL;
49 
50 	*hdr = (struct nhrp_packet_header) {
51 		.afnum = htons(family2afi(sockunion_family(src_nbma))),
52 		.protocol_type = htons(family2proto(sockunion_family(src_proto))),
53 		.version = NHRP_VERSION_RFC2332,
54 		.type = type,
55 		.hop_count = 64,
56 		.src_nbma_address_len = sockunion_get_addrlen(src_nbma),
57 		.src_protocol_address_len = sockunion_get_addrlen(src_proto),
58 		.dst_protocol_address_len = sockunion_get_addrlen(dst_proto),
59 	};
60 
61 	zbuf_put(zb, sockunion_get_addr(src_nbma), hdr->src_nbma_address_len);
62 	zbuf_put(zb, sockunion_get_addr(src_proto), hdr->src_protocol_address_len);
63 	zbuf_put(zb, sockunion_get_addr(dst_proto), hdr->dst_protocol_address_len);
64 
65 	return hdr;
66 }
67 
nhrp_packet_pull(struct zbuf * zb,union sockunion * src_nbma,union sockunion * src_proto,union sockunion * dst_proto)68 struct nhrp_packet_header *nhrp_packet_pull(
69 	struct zbuf *zb,
70 	union sockunion *src_nbma,
71 	union sockunion *src_proto,
72 	union sockunion *dst_proto)
73 {
74 	struct nhrp_packet_header *hdr;
75 
76 	hdr = zbuf_pull(zb, struct nhrp_packet_header);
77 	if (!hdr) return NULL;
78 
79 	sockunion_set(
80 		src_nbma, afi2family(htons(hdr->afnum)),
81 		zbuf_pulln(zb, hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len),
82 		hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len);
83 	sockunion_set(
84 		src_proto, proto2family(htons(hdr->protocol_type)),
85 		zbuf_pulln(zb, hdr->src_protocol_address_len),
86 		hdr->src_protocol_address_len);
87 	sockunion_set(
88 		dst_proto, proto2family(htons(hdr->protocol_type)),
89 		zbuf_pulln(zb, hdr->dst_protocol_address_len),
90 		hdr->dst_protocol_address_len);
91 
92 	return hdr;
93 }
94 
nhrp_packet_calculate_checksum(const uint8_t * pdu,uint16_t len)95 uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len)
96 {
97 	const uint16_t *pdu16 = (const uint16_t *) pdu;
98 	uint32_t csum = 0;
99 	int i;
100 
101 	for (i = 0; i < len / 2; i++)
102 		csum += pdu16[i];
103 	if (len & 1)
104 		csum += htons(pdu[len - 1]);
105 
106 	while (csum & 0xffff0000)
107 		csum = (csum & 0xffff) + (csum >> 16);
108 
109 	return (~csum) & 0xffff;
110 }
111 
nhrp_packet_complete(struct zbuf * zb,struct nhrp_packet_header * hdr)112 void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr)
113 {
114 	unsigned short size;
115 
116 	if (hdr->extension_offset)
117 		nhrp_ext_push(zb, hdr, NHRP_EXTENSION_END | NHRP_EXTENSION_FLAG_COMPULSORY);
118 
119 	size = zb->tail - (uint8_t *)hdr;
120 	hdr->packet_size = htons(size);
121 	hdr->checksum = 0;
122 	hdr->checksum = nhrp_packet_calculate_checksum((uint8_t *) hdr, size);
123 }
124 
nhrp_cie_push(struct zbuf * zb,uint8_t code,const union sockunion * nbma,const union sockunion * proto)125 struct nhrp_cie_header *nhrp_cie_push(
126 	struct zbuf *zb,
127 	uint8_t code,
128 	const union sockunion *nbma,
129 	const union sockunion *proto)
130 {
131 	struct nhrp_cie_header *cie;
132 
133 	cie = zbuf_push(zb, struct nhrp_cie_header);
134 	*cie = (struct nhrp_cie_header) {
135 		.code = code,
136 	};
137 	if (nbma) {
138 		cie->nbma_address_len = sockunion_get_addrlen(nbma);
139 		zbuf_put(zb, sockunion_get_addr(nbma), cie->nbma_address_len);
140 	}
141 	if (proto) {
142 		cie->protocol_address_len = sockunion_get_addrlen(proto);
143 		zbuf_put(zb, sockunion_get_addr(proto), cie->protocol_address_len);
144 	}
145 
146 	return cie;
147 }
148 
nhrp_cie_pull(struct zbuf * zb,struct nhrp_packet_header * hdr,union sockunion * nbma,union sockunion * proto)149 struct nhrp_cie_header *nhrp_cie_pull(
150 	struct zbuf *zb,
151 	struct nhrp_packet_header *hdr,
152 	union sockunion *nbma,
153 	union sockunion *proto)
154 {
155 	struct nhrp_cie_header *cie;
156 
157 	cie = zbuf_pull(zb, struct nhrp_cie_header);
158 	if (!cie) return NULL;
159 
160 	if (cie->nbma_address_len + cie->nbma_subaddress_len) {
161 		sockunion_set(
162 			nbma, afi2family(htons(hdr->afnum)),
163 			zbuf_pulln(zb, cie->nbma_address_len + cie->nbma_subaddress_len),
164 			cie->nbma_address_len + cie->nbma_subaddress_len);
165 	} else {
166 		sockunion_family(nbma) = AF_UNSPEC;
167 	}
168 
169 	if (cie->protocol_address_len) {
170 		sockunion_set(
171 			proto, proto2family(htons(hdr->protocol_type)),
172 			zbuf_pulln(zb, cie->protocol_address_len),
173 			cie->protocol_address_len);
174 	} else {
175 		sockunion_family(proto) = AF_UNSPEC;
176 	}
177 
178 	return cie;
179 }
180 
nhrp_ext_push(struct zbuf * zb,struct nhrp_packet_header * hdr,uint16_t type)181 struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type)
182 {
183 	struct nhrp_extension_header *ext;
184 	ext = zbuf_push(zb, struct nhrp_extension_header);
185 	if (!ext) return NULL;
186 
187 	if (!hdr->extension_offset)
188 		hdr->extension_offset = htons(zb->tail - (uint8_t*) hdr - sizeof(struct nhrp_extension_header));
189 
190 	*ext = (struct nhrp_extension_header) {
191 		.type = htons(type),
192 		.length = 0,
193 	};
194 	return ext;
195 }
196 
nhrp_ext_complete(struct zbuf * zb,struct nhrp_extension_header * ext)197 void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext)
198 {
199 	ext->length = htons(zb->tail - (uint8_t*)ext - sizeof(struct nhrp_extension_header));
200 }
201 
nhrp_ext_pull(struct zbuf * zb,struct zbuf * payload)202 struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload)
203 {
204 	struct nhrp_extension_header *ext;
205 	uint16_t plen;
206 
207 	ext = zbuf_pull(zb, struct nhrp_extension_header);
208 	if (!ext) return NULL;
209 
210 	plen = htons(ext->length);
211 	zbuf_init(payload, zbuf_pulln(zb, plen), plen, plen);
212 	return ext;
213 }
214 
nhrp_ext_request(struct zbuf * zb,struct nhrp_packet_header * hdr,struct interface * ifp)215 void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp)
216 {
217 	/* Place holders for standard extensions */
218 	nhrp_ext_push(zb, hdr, NHRP_EXTENSION_FORWARD_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
219 	nhrp_ext_push(zb, hdr, NHRP_EXTENSION_REVERSE_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
220 	nhrp_ext_push(zb, hdr, NHRP_EXTENSION_RESPONDER_ADDRESS | NHRP_EXTENSION_FLAG_COMPULSORY);
221 }
222 
nhrp_ext_reply(struct zbuf * zb,struct nhrp_packet_header * hdr,struct interface * ifp,struct nhrp_extension_header * ext,struct zbuf * extpayload)223 int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload)
224 {
225 	struct nhrp_interface *nifp = ifp->info;
226 	struct nhrp_afi_data *ad = &nifp->afi[htons(hdr->afnum)];
227 	struct nhrp_extension_header *dst;
228 	struct nhrp_cie_header *cie;
229 	uint16_t type;
230 
231 	type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
232 	if (type == NHRP_EXTENSION_END)
233 		return 0;
234 
235 	dst = nhrp_ext_push(zb, hdr, htons(ext->type));
236 	if (!dst) goto err;
237 
238 	switch (type) {
239 	case NHRP_EXTENSION_RESPONDER_ADDRESS:
240 		cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &ad->addr);
241 		if (!cie) goto err;
242 		cie->holding_time = htons(ad->holdtime);
243 		break;
244 	default:
245 		if (type & NHRP_EXTENSION_FLAG_COMPULSORY)
246 			goto err;
247 	case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
248 	case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
249 		/* Supported compulsory extensions, and any
250 		 * non-compulsory that is not explicitly handled,
251 		 * should be just copied. */
252 		zbuf_copy(zb, extpayload, zbuf_used(extpayload));
253 		break;
254 	}
255 	nhrp_ext_complete(zb, dst);
256 	return 0;
257 err:
258 	zbuf_set_werror(zb);
259 	return -1;
260 }
261 
nhrp_packet_recvraw(struct thread * t)262 static int nhrp_packet_recvraw(struct thread *t)
263 {
264 	int fd = THREAD_FD(t), ifindex;
265 	struct zbuf *zb;
266 	struct interface *ifp;
267 	struct nhrp_peer *p;
268 	union sockunion remote_nbma;
269 	uint8_t addr[64];
270 	size_t len, addrlen;
271 
272 	thread_add_read(master, nhrp_packet_recvraw, 0, fd);
273 
274 	zb = zbuf_alloc(1500);
275 	if (!zb) return 0;
276 
277 	len = zbuf_size(zb);
278 	addrlen = sizeof(addr);
279 	if (os_recvmsg(zb->buf, &len, &ifindex, addr, &addrlen) < 0)
280 		goto err;
281 
282 	zb->head = zb->buf;
283 	zb->tail = zb->buf + len;
284 
285 	switch (addrlen) {
286 	case 4:
287 		sockunion_set(&remote_nbma, AF_INET, addr, addrlen);
288 		break;
289 	default:
290 		goto err;
291 	}
292 
293 	ifp = if_lookup_by_index(ifindex);
294 	if (!ifp) goto err;
295 
296 	p = nhrp_peer_get(ifp, &remote_nbma);
297 	if (!p) goto err;
298 
299 	nhrp_peer_recv(p, zb);
300 	nhrp_peer_unref(p);
301 	return 0;
302 
303 err:
304 	zbuf_free(zb);
305 	return 0;
306 }
307 
nhrp_packet_init(void)308 int nhrp_packet_init(void)
309 {
310 	thread_add_read(master, nhrp_packet_recvraw, 0, os_socket());
311 	return 0;
312 }
313