1 |
|
|
/* $OpenBSD: packet.c,v 1.70 2017/03/04 00:06:10 renato Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2013, 2016 Renato Westphal <renato@openbsd.org> |
5 |
|
|
* Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> |
6 |
|
|
* Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org> |
7 |
|
|
* |
8 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
9 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
10 |
|
|
* copyright notice and this permission notice appear in all copies. |
11 |
|
|
* |
12 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
13 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
15 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
18 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include <sys/types.h> |
22 |
|
|
#include <netinet/in.h> |
23 |
|
|
#include <netinet/tcp.h> |
24 |
|
|
#include <arpa/inet.h> |
25 |
|
|
#include <net/if_dl.h> |
26 |
|
|
#include <unistd.h> |
27 |
|
|
#include <errno.h> |
28 |
|
|
#include <stdlib.h> |
29 |
|
|
#include <string.h> |
30 |
|
|
|
31 |
|
|
#include "ldpd.h" |
32 |
|
|
#include "ldpe.h" |
33 |
|
|
#include "log.h" |
34 |
|
|
|
35 |
|
|
static struct iface *disc_find_iface(unsigned int, int, |
36 |
|
|
union ldpd_addr *, int); |
37 |
|
|
static void session_read(int, short, void *); |
38 |
|
|
static void session_write(int, short, void *); |
39 |
|
|
static ssize_t session_get_pdu(struct ibuf_read *, char **); |
40 |
|
|
static void tcp_close(struct tcp_conn *); |
41 |
|
|
static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *); |
42 |
|
|
static void pending_conn_timeout(int, short, void *); |
43 |
|
|
|
44 |
|
|
int |
45 |
|
|
gen_ldp_hdr(struct ibuf *buf, uint16_t size) |
46 |
|
|
{ |
47 |
|
|
struct ldp_hdr ldp_hdr; |
48 |
|
|
|
49 |
|
|
memset(&ldp_hdr, 0, sizeof(ldp_hdr)); |
50 |
|
|
ldp_hdr.version = htons(LDP_VERSION); |
51 |
|
|
/* exclude the 'Version' and 'PDU Length' fields from the total */ |
52 |
|
|
ldp_hdr.length = htons(size - LDP_HDR_DEAD_LEN); |
53 |
|
|
ldp_hdr.lsr_id = leconf->rtr_id.s_addr; |
54 |
|
|
ldp_hdr.lspace_id = 0; |
55 |
|
|
|
56 |
|
|
return (ibuf_add(buf, &ldp_hdr, LDP_HDR_SIZE)); |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
int |
60 |
|
|
gen_msg_hdr(struct ibuf *buf, uint16_t type, uint16_t size) |
61 |
|
|
{ |
62 |
|
|
static int msgcnt = 0; |
63 |
|
|
struct ldp_msg msg; |
64 |
|
|
|
65 |
|
|
memset(&msg, 0, sizeof(msg)); |
66 |
|
|
msg.type = htons(type); |
67 |
|
|
/* exclude the 'Type' and 'Length' fields from the total */ |
68 |
|
|
msg.length = htons(size - LDP_MSG_DEAD_LEN); |
69 |
|
|
msg.id = htonl(++msgcnt); |
70 |
|
|
|
71 |
|
|
return (ibuf_add(buf, &msg, sizeof(msg))); |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
/* send packets */ |
75 |
|
|
int |
76 |
|
|
send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia, |
77 |
|
|
void *pkt, size_t len) |
78 |
|
|
{ |
79 |
|
|
struct sockaddr *sa; |
80 |
|
|
|
81 |
|
|
switch (af) { |
82 |
|
|
case AF_INET: |
83 |
|
|
if (ia && IN_MULTICAST(ntohl(dst->v4.s_addr))) { |
84 |
|
|
/* set outgoing interface for multicast traffic */ |
85 |
|
|
if (sock_set_ipv4_mcast(ia->iface) == -1) { |
86 |
|
|
log_debug("%s: error setting multicast " |
87 |
|
|
"interface, %s", __func__, ia->iface->name); |
88 |
|
|
return (-1); |
89 |
|
|
} |
90 |
|
|
} |
91 |
|
|
break; |
92 |
|
|
case AF_INET6: |
93 |
|
|
if (ia && IN6_IS_ADDR_MULTICAST(&dst->v6)) { |
94 |
|
|
/* set outgoing interface for multicast traffic */ |
95 |
|
|
if (sock_set_ipv6_mcast(ia->iface) == -1) { |
96 |
|
|
log_debug("%s: error setting multicast " |
97 |
|
|
"interface, %s", __func__, ia->iface->name); |
98 |
|
|
return (-1); |
99 |
|
|
} |
100 |
|
|
} |
101 |
|
|
break; |
102 |
|
|
default: |
103 |
|
|
fatalx("send_packet: unknown af"); |
104 |
|
|
} |
105 |
|
|
|
106 |
|
|
sa = addr2sa(af, dst, LDP_PORT); |
107 |
|
|
if (sendto(fd, pkt, len, 0, sa, sa->sa_len) == -1) { |
108 |
|
|
log_warn("%s: error sending packet to %s", __func__, |
109 |
|
|
log_sockaddr(sa)); |
110 |
|
|
return (-1); |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
return (0); |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
/* Discovery functions */ |
117 |
|
|
#define CMSG_MAXLEN max(sizeof(struct sockaddr_dl), sizeof(struct in6_pktinfo)) |
118 |
|
|
void |
119 |
|
|
disc_recv_packet(int fd, short event, void *bula) |
120 |
|
|
{ |
121 |
|
|
union { |
122 |
|
|
struct cmsghdr hdr; |
123 |
|
|
char buf[CMSG_SPACE(CMSG_MAXLEN)]; |
124 |
|
|
} cmsgbuf; |
125 |
|
|
struct msghdr m; |
126 |
|
|
struct sockaddr_storage from; |
127 |
|
|
struct iovec iov; |
128 |
|
|
char *buf; |
129 |
|
|
struct cmsghdr *cmsg; |
130 |
|
|
ssize_t r; |
131 |
|
|
int multicast; |
132 |
|
|
int af; |
133 |
|
|
union ldpd_addr src; |
134 |
|
|
unsigned int ifindex = 0; |
135 |
|
|
struct iface *iface; |
136 |
|
|
uint16_t len; |
137 |
|
|
struct ldp_hdr ldp_hdr; |
138 |
|
|
uint16_t pdu_len; |
139 |
|
|
struct ldp_msg msg; |
140 |
|
|
uint16_t msg_len; |
141 |
|
|
struct in_addr lsr_id; |
142 |
|
|
|
143 |
|
|
if (event != EV_READ) |
144 |
|
|
return; |
145 |
|
|
|
146 |
|
|
/* setup buffer */ |
147 |
|
|
memset(&m, 0, sizeof(m)); |
148 |
|
|
iov.iov_base = buf = pkt_ptr; |
149 |
|
|
iov.iov_len = IBUF_READ_SIZE; |
150 |
|
|
m.msg_name = &from; |
151 |
|
|
m.msg_namelen = sizeof(from); |
152 |
|
|
m.msg_iov = &iov; |
153 |
|
|
m.msg_iovlen = 1; |
154 |
|
|
m.msg_control = &cmsgbuf.buf; |
155 |
|
|
m.msg_controllen = sizeof(cmsgbuf.buf); |
156 |
|
|
|
157 |
|
|
if ((r = recvmsg(fd, &m, 0)) == -1) { |
158 |
|
|
if (errno != EAGAIN && errno != EINTR) |
159 |
|
|
log_debug("%s: read error: %s", __func__, |
160 |
|
|
strerror(errno)); |
161 |
|
|
return; |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0; |
165 |
|
|
sa2addr((struct sockaddr *)&from, &af, &src); |
166 |
|
|
if (bad_addr(af, &src)) { |
167 |
|
|
log_debug("%s: invalid source address: %s", __func__, |
168 |
|
|
log_addr(af, &src)); |
169 |
|
|
return; |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; |
173 |
|
|
cmsg = CMSG_NXTHDR(&m, cmsg)) { |
174 |
|
|
if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && |
175 |
|
|
cmsg->cmsg_type == IP_RECVIF) { |
176 |
|
|
ifindex = ((struct sockaddr_dl *) |
177 |
|
|
CMSG_DATA(cmsg))->sdl_index; |
178 |
|
|
break; |
179 |
|
|
} |
180 |
|
|
if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 && |
181 |
|
|
cmsg->cmsg_type == IPV6_PKTINFO) { |
182 |
|
|
ifindex = ((struct in6_pktinfo *) |
183 |
|
|
CMSG_DATA(cmsg))->ipi6_ifindex; |
184 |
|
|
break; |
185 |
|
|
} |
186 |
|
|
} |
187 |
|
|
|
188 |
|
|
/* find a matching interface */ |
189 |
|
|
iface = disc_find_iface(ifindex, af, &src, multicast); |
190 |
|
|
if (iface == NULL) |
191 |
|
|
return; |
192 |
|
|
|
193 |
|
|
/* check packet size */ |
194 |
|
|
len = (uint16_t)r; |
195 |
|
|
if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) { |
196 |
|
|
log_debug("%s: bad packet size, source %s", __func__, |
197 |
|
|
log_addr(af, &src)); |
198 |
|
|
return; |
199 |
|
|
} |
200 |
|
|
|
201 |
|
|
/* LDP header sanity checks */ |
202 |
|
|
memcpy(&ldp_hdr, buf, sizeof(ldp_hdr)); |
203 |
|
|
if (ntohs(ldp_hdr.version) != LDP_VERSION) { |
204 |
|
|
log_debug("%s: invalid LDP version %d, source %s", __func__, |
205 |
|
|
ntohs(ldp_hdr.version), log_addr(af, &src)); |
206 |
|
|
return; |
207 |
|
|
} |
208 |
|
|
if (ntohs(ldp_hdr.lspace_id) != 0) { |
209 |
|
|
log_debug("%s: invalid label space %u, source %s", __func__, |
210 |
|
|
ntohs(ldp_hdr.lspace_id), log_addr(af, &src)); |
211 |
|
|
return; |
212 |
|
|
} |
213 |
|
|
/* check "PDU Length" field */ |
214 |
|
|
pdu_len = ntohs(ldp_hdr.length); |
215 |
|
|
if ((pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE)) || |
216 |
|
|
(pdu_len > (len - LDP_HDR_DEAD_LEN))) { |
217 |
|
|
log_debug("%s: invalid LDP packet length %u, source %s", |
218 |
|
|
__func__, ntohs(ldp_hdr.length), log_addr(af, &src)); |
219 |
|
|
return; |
220 |
|
|
} |
221 |
|
|
buf += LDP_HDR_SIZE; |
222 |
|
|
len -= LDP_HDR_SIZE; |
223 |
|
|
|
224 |
|
|
lsr_id.s_addr = ldp_hdr.lsr_id; |
225 |
|
|
|
226 |
|
|
/* |
227 |
|
|
* For UDP, we process only the first message of each packet. This does |
228 |
|
|
* not impose any restrictions since LDP uses UDP only for sending Hello |
229 |
|
|
* packets. |
230 |
|
|
*/ |
231 |
|
|
memcpy(&msg, buf, sizeof(msg)); |
232 |
|
|
|
233 |
|
|
/* check "Message Length" field */ |
234 |
|
|
msg_len = ntohs(msg.length); |
235 |
|
|
if (msg_len < LDP_MSG_LEN || ((msg_len + LDP_MSG_DEAD_LEN) > pdu_len)) { |
236 |
|
|
log_debug("%s: invalid LDP message length %u, source %s", |
237 |
|
|
__func__, ntohs(msg.length), log_addr(af, &src)); |
238 |
|
|
return; |
239 |
|
|
} |
240 |
|
|
buf += LDP_MSG_SIZE; |
241 |
|
|
len -= LDP_MSG_SIZE; |
242 |
|
|
|
243 |
|
|
/* switch LDP packet type */ |
244 |
|
|
switch (ntohs(msg.type)) { |
245 |
|
|
case MSG_TYPE_HELLO: |
246 |
|
|
recv_hello(lsr_id, &msg, af, &src, iface, multicast, buf, len); |
247 |
|
|
break; |
248 |
|
|
default: |
249 |
|
|
log_debug("%s: unknown LDP packet type, source %s", __func__, |
250 |
|
|
log_addr(af, &src)); |
251 |
|
|
} |
252 |
|
|
} |
253 |
|
|
|
254 |
|
|
static struct iface * |
255 |
|
|
disc_find_iface(unsigned int ifindex, int af, union ldpd_addr *src, |
256 |
|
|
int multicast) |
257 |
|
|
{ |
258 |
|
|
struct iface *iface; |
259 |
|
|
struct iface_af *ia; |
260 |
|
|
struct if_addr *if_addr; |
261 |
|
|
in_addr_t mask; |
262 |
|
|
|
263 |
|
|
iface = if_lookup(leconf, ifindex); |
264 |
|
|
if (iface == NULL) |
265 |
|
|
return (NULL); |
266 |
|
|
|
267 |
|
|
/* |
268 |
|
|
* For unicast packets, we just need to make sure that the interface |
269 |
|
|
* is enabled for the given address-family. |
270 |
|
|
*/ |
271 |
|
|
if (!multicast) { |
272 |
|
|
ia = iface_af_get(iface, af); |
273 |
|
|
if (ia->enabled) |
274 |
|
|
return (iface); |
275 |
|
|
return (NULL); |
276 |
|
|
} |
277 |
|
|
|
278 |
|
|
switch (af) { |
279 |
|
|
case AF_INET: |
280 |
|
|
LIST_FOREACH(if_addr, &iface->addr_list, entry) { |
281 |
|
|
if (if_addr->af != AF_INET) |
282 |
|
|
continue; |
283 |
|
|
|
284 |
|
|
switch (iface->type) { |
285 |
|
|
case IF_TYPE_POINTOPOINT: |
286 |
|
|
if (if_addr->dstbrd.v4.s_addr == src->v4.s_addr) |
287 |
|
|
return (iface); |
288 |
|
|
break; |
289 |
|
|
default: |
290 |
|
|
mask = prefixlen2mask(if_addr->prefixlen); |
291 |
|
|
if ((if_addr->addr.v4.s_addr & mask) == |
292 |
|
|
(src->v4.s_addr & mask)) |
293 |
|
|
return (iface); |
294 |
|
|
break; |
295 |
|
|
} |
296 |
|
|
} |
297 |
|
|
break; |
298 |
|
|
case AF_INET6: |
299 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&src->v6)) |
300 |
|
|
return (iface); |
301 |
|
|
break; |
302 |
|
|
default: |
303 |
|
|
fatalx("disc_find_iface: unknown af"); |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
return (NULL); |
307 |
|
|
} |
308 |
|
|
|
309 |
|
|
void |
310 |
|
|
session_accept(int fd, short event, void *bula) |
311 |
|
|
{ |
312 |
|
|
struct sockaddr_storage src; |
313 |
|
|
socklen_t len = sizeof(src); |
314 |
|
|
int newfd; |
315 |
|
|
int af; |
316 |
|
|
union ldpd_addr addr; |
317 |
|
|
struct nbr *nbr; |
318 |
|
|
struct pending_conn *pconn; |
319 |
|
|
|
320 |
|
|
if (!(event & EV_READ)) |
321 |
|
|
return; |
322 |
|
|
|
323 |
|
|
newfd = accept4(fd, (struct sockaddr *)&src, &len, |
324 |
|
|
SOCK_NONBLOCK | SOCK_CLOEXEC); |
325 |
|
|
if (newfd == -1) { |
326 |
|
|
/* |
327 |
|
|
* Pause accept if we are out of file descriptors, or |
328 |
|
|
* libevent will haunt us here too. |
329 |
|
|
*/ |
330 |
|
|
if (errno == ENFILE || errno == EMFILE) { |
331 |
|
|
accept_pause(); |
332 |
|
|
} else if (errno != EWOULDBLOCK && errno != EINTR && |
333 |
|
|
errno != ECONNABORTED) |
334 |
|
|
log_debug("%s: accept error: %s", __func__, |
335 |
|
|
strerror(errno)); |
336 |
|
|
return; |
337 |
|
|
} |
338 |
|
|
|
339 |
|
|
sa2addr((struct sockaddr *)&src, &af, &addr); |
340 |
|
|
|
341 |
|
|
/* |
342 |
|
|
* Since we don't support label spaces, we can identify this neighbor |
343 |
|
|
* just by its source address. This way we don't need to wait for its |
344 |
|
|
* Initialization message to know who we are talking to. |
345 |
|
|
*/ |
346 |
|
|
nbr = nbr_find_addr(af, &addr); |
347 |
|
|
if (nbr == NULL) { |
348 |
|
|
/* |
349 |
|
|
* According to RFC 5036, we would need to send a No Hello |
350 |
|
|
* Error Notification message and close this TCP connection |
351 |
|
|
* right now. But doing so would trigger the backoff exponential |
352 |
|
|
* timer in the remote peer, which would considerably slow down |
353 |
|
|
* the session establishment process. The trick here is to wait |
354 |
|
|
* five seconds before sending the Notification Message. There's |
355 |
|
|
* a good chance that the remote peer will send us a Hello |
356 |
|
|
* message within this interval, so it's worth waiting before |
357 |
|
|
* taking a more drastic measure. |
358 |
|
|
*/ |
359 |
|
|
pconn = pending_conn_find(af, &addr); |
360 |
|
|
if (pconn) |
361 |
|
|
close(newfd); |
362 |
|
|
else |
363 |
|
|
pending_conn_new(newfd, af, &addr); |
364 |
|
|
return; |
365 |
|
|
} |
366 |
|
|
/* protection against buggy implementations */ |
367 |
|
|
if (nbr_session_active_role(nbr)) { |
368 |
|
|
close(newfd); |
369 |
|
|
return; |
370 |
|
|
} |
371 |
|
|
if (nbr->state != NBR_STA_PRESENT) { |
372 |
|
|
log_debug("%s: lsr-id %s: rejecting additional transport " |
373 |
|
|
"connection", __func__, inet_ntoa(nbr->id)); |
374 |
|
|
close(newfd); |
375 |
|
|
return; |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
session_accept_nbr(nbr, newfd); |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
void |
382 |
|
|
session_accept_nbr(struct nbr *nbr, int fd) |
383 |
|
|
{ |
384 |
|
|
struct nbr_params *nbrp; |
385 |
|
|
int opt; |
386 |
|
|
socklen_t len; |
387 |
|
|
|
388 |
|
|
nbrp = nbr_params_find(leconf, nbr->id); |
389 |
|
|
if (nbr_gtsm_check(fd, nbr, nbrp)) { |
390 |
|
|
close(fd); |
391 |
|
|
return; |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
if (nbrp && nbrp->auth.method == AUTH_MD5SIG) { |
395 |
|
|
if (sysdep.no_pfkey || sysdep.no_md5sig) { |
396 |
|
|
log_warnx("md5sig configured but not available"); |
397 |
|
|
close(fd); |
398 |
|
|
return; |
399 |
|
|
} |
400 |
|
|
|
401 |
|
|
len = sizeof(opt); |
402 |
|
|
if (getsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, &len) == -1) |
403 |
|
|
fatal("getsockopt TCP_MD5SIG"); |
404 |
|
|
if (!opt) { /* non-md5'd connection! */ |
405 |
|
|
log_warnx("connection attempt without md5 signature"); |
406 |
|
|
close(fd); |
407 |
|
|
return; |
408 |
|
|
} |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
nbr->tcp = tcp_new(fd, nbr); |
412 |
|
|
nbr_fsm(nbr, NBR_EVT_MATCH_ADJ); |
413 |
|
|
} |
414 |
|
|
|
415 |
|
|
static void |
416 |
|
|
session_read(int fd, short event, void *arg) |
417 |
|
|
{ |
418 |
|
|
struct nbr *nbr = arg; |
419 |
|
|
struct tcp_conn *tcp = nbr->tcp; |
420 |
|
|
struct ldp_hdr *ldp_hdr; |
421 |
|
|
struct ldp_msg *msg; |
422 |
|
|
char *buf, *pdu; |
423 |
|
|
ssize_t n, len; |
424 |
|
|
uint16_t pdu_len, msg_len, msg_size, max_pdu_len; |
425 |
|
|
int ret; |
426 |
|
|
|
427 |
|
|
if (event != EV_READ) |
428 |
|
|
return; |
429 |
|
|
|
430 |
|
|
if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos, |
431 |
|
|
sizeof(tcp->rbuf->buf) - tcp->rbuf->wpos)) == -1) { |
432 |
|
|
if (errno != EINTR && errno != EAGAIN) { |
433 |
|
|
log_warn("%s: read error", __func__); |
434 |
|
|
nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); |
435 |
|
|
return; |
436 |
|
|
} |
437 |
|
|
/* retry read */ |
438 |
|
|
return; |
439 |
|
|
} |
440 |
|
|
if (n == 0) { |
441 |
|
|
/* connection closed */ |
442 |
|
|
log_debug("%s: connection closed by remote end", __func__); |
443 |
|
|
nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); |
444 |
|
|
return; |
445 |
|
|
} |
446 |
|
|
tcp->rbuf->wpos += n; |
447 |
|
|
|
448 |
|
|
while ((len = session_get_pdu(tcp->rbuf, &buf)) > 0) { |
449 |
|
|
pdu = buf; |
450 |
|
|
ldp_hdr = (struct ldp_hdr *)pdu; |
451 |
|
|
if (ntohs(ldp_hdr->version) != LDP_VERSION) { |
452 |
|
|
session_shutdown(nbr, S_BAD_PROTO_VER, 0, 0); |
453 |
|
|
free(buf); |
454 |
|
|
return; |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
pdu_len = ntohs(ldp_hdr->length); |
458 |
|
|
/* |
459 |
|
|
* RFC 5036 - Section 3.5.3: |
460 |
|
|
* "Prior to completion of the negotiation, the maximum |
461 |
|
|
* allowable length is 4096 bytes". |
462 |
|
|
*/ |
463 |
|
|
if (nbr->state == NBR_STA_OPER) |
464 |
|
|
max_pdu_len = nbr->max_pdu_len; |
465 |
|
|
else |
466 |
|
|
max_pdu_len = LDP_MAX_LEN; |
467 |
|
|
if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) || |
468 |
|
|
pdu_len > max_pdu_len) { |
469 |
|
|
session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); |
470 |
|
|
free(buf); |
471 |
|
|
return; |
472 |
|
|
} |
473 |
|
|
pdu_len -= LDP_HDR_PDU_LEN; |
474 |
|
|
if (ldp_hdr->lsr_id != nbr->id.s_addr || |
475 |
|
|
ldp_hdr->lspace_id != 0) { |
476 |
|
|
session_shutdown(nbr, S_BAD_LDP_ID, 0, 0); |
477 |
|
|
free(buf); |
478 |
|
|
return; |
479 |
|
|
} |
480 |
|
|
pdu += LDP_HDR_SIZE; |
481 |
|
|
len -= LDP_HDR_SIZE; |
482 |
|
|
|
483 |
|
|
nbr_fsm(nbr, NBR_EVT_PDU_RCVD); |
484 |
|
|
|
485 |
|
|
while (len >= LDP_MSG_SIZE) { |
486 |
|
|
uint16_t type; |
487 |
|
|
|
488 |
|
|
msg = (struct ldp_msg *)pdu; |
489 |
|
|
type = ntohs(msg->type); |
490 |
|
|
msg_len = ntohs(msg->length); |
491 |
|
|
if (msg_len < LDP_MSG_LEN || |
492 |
|
|
(msg_len + LDP_MSG_DEAD_LEN) > pdu_len) { |
493 |
|
|
session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, |
494 |
|
|
msg->type); |
495 |
|
|
free(buf); |
496 |
|
|
return; |
497 |
|
|
} |
498 |
|
|
msg_size = msg_len + LDP_MSG_DEAD_LEN; |
499 |
|
|
pdu_len -= msg_size; |
500 |
|
|
|
501 |
|
|
/* check for error conditions earlier */ |
502 |
|
|
switch (type) { |
503 |
|
|
case MSG_TYPE_INIT: |
504 |
|
|
if ((nbr->state != NBR_STA_INITIAL) && |
505 |
|
|
(nbr->state != NBR_STA_OPENSENT)) { |
506 |
|
|
session_shutdown(nbr, S_SHUTDOWN, |
507 |
|
|
msg->id, msg->type); |
508 |
|
|
free(buf); |
509 |
|
|
return; |
510 |
|
|
} |
511 |
|
|
break; |
512 |
|
|
case MSG_TYPE_KEEPALIVE: |
513 |
|
|
if ((nbr->state == NBR_STA_INITIAL) || |
514 |
|
|
(nbr->state == NBR_STA_OPENSENT)) { |
515 |
|
|
session_shutdown(nbr, S_SHUTDOWN, |
516 |
|
|
msg->id, msg->type); |
517 |
|
|
free(buf); |
518 |
|
|
return; |
519 |
|
|
} |
520 |
|
|
break; |
521 |
|
|
default: |
522 |
|
|
if (nbr->state != NBR_STA_OPER) { |
523 |
|
|
session_shutdown(nbr, S_SHUTDOWN, |
524 |
|
|
msg->id, msg->type); |
525 |
|
|
free(buf); |
526 |
|
|
return; |
527 |
|
|
} |
528 |
|
|
break; |
529 |
|
|
} |
530 |
|
|
|
531 |
|
|
/* switch LDP packet type */ |
532 |
|
|
switch (type) { |
533 |
|
|
case MSG_TYPE_NOTIFICATION: |
534 |
|
|
ret = recv_notification(nbr, pdu, msg_size); |
535 |
|
|
break; |
536 |
|
|
case MSG_TYPE_INIT: |
537 |
|
|
ret = recv_init(nbr, pdu, msg_size); |
538 |
|
|
break; |
539 |
|
|
case MSG_TYPE_KEEPALIVE: |
540 |
|
|
ret = recv_keepalive(nbr, pdu, msg_size); |
541 |
|
|
break; |
542 |
|
|
case MSG_TYPE_CAPABILITY: |
543 |
|
|
ret = recv_capability(nbr, pdu, msg_size); |
544 |
|
|
break; |
545 |
|
|
case MSG_TYPE_ADDR: |
546 |
|
|
case MSG_TYPE_ADDRWITHDRAW: |
547 |
|
|
ret = recv_address(nbr, pdu, msg_size); |
548 |
|
|
break; |
549 |
|
|
case MSG_TYPE_LABELMAPPING: |
550 |
|
|
case MSG_TYPE_LABELREQUEST: |
551 |
|
|
case MSG_TYPE_LABELWITHDRAW: |
552 |
|
|
case MSG_TYPE_LABELRELEASE: |
553 |
|
|
case MSG_TYPE_LABELABORTREQ: |
554 |
|
|
ret = recv_labelmessage(nbr, pdu, msg_size, |
555 |
|
|
type); |
556 |
|
|
break; |
557 |
|
|
default: |
558 |
|
|
log_debug("%s: unknown LDP message from nbr %s", |
559 |
|
|
__func__, inet_ntoa(nbr->id)); |
560 |
|
|
if (!(ntohs(msg->type) & UNKNOWN_FLAG)) |
561 |
|
|
send_notification(nbr->tcp, |
562 |
|
|
S_UNKNOWN_MSG, msg->id, msg->type); |
563 |
|
|
/* ignore the message */ |
564 |
|
|
ret = 0; |
565 |
|
|
break; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
if (ret == -1) { |
569 |
|
|
/* parser failed, giving up */ |
570 |
|
|
free(buf); |
571 |
|
|
return; |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
/* Analyse the next message */ |
575 |
|
|
pdu += msg_size; |
576 |
|
|
len -= msg_size; |
577 |
|
|
} |
578 |
|
|
free(buf); |
579 |
|
|
if (len != 0) { |
580 |
|
|
session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); |
581 |
|
|
return; |
582 |
|
|
} |
583 |
|
|
} |
584 |
|
|
} |
585 |
|
|
|
586 |
|
|
static void |
587 |
|
|
session_write(int fd, short event, void *arg) |
588 |
|
|
{ |
589 |
|
|
struct tcp_conn *tcp = arg; |
590 |
|
|
struct nbr *nbr = tcp->nbr; |
591 |
|
|
|
592 |
|
|
if (!(event & EV_WRITE)) |
593 |
|
|
return; |
594 |
|
|
|
595 |
|
|
if (msgbuf_write(&tcp->wbuf.wbuf) <= 0) |
596 |
|
|
if (errno != EAGAIN && nbr) |
597 |
|
|
nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); |
598 |
|
|
|
599 |
|
|
if (nbr == NULL && !tcp->wbuf.wbuf.queued) { |
600 |
|
|
/* |
601 |
|
|
* We are done sending the notification message, now we can |
602 |
|
|
* close the socket. |
603 |
|
|
*/ |
604 |
|
|
tcp_close(tcp); |
605 |
|
|
return; |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
evbuf_event_add(&tcp->wbuf); |
609 |
|
|
} |
610 |
|
|
|
611 |
|
|
void |
612 |
|
|
session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, |
613 |
|
|
uint32_t msg_type) |
614 |
|
|
{ |
615 |
|
|
switch (nbr->state) { |
616 |
|
|
case NBR_STA_PRESENT: |
617 |
|
|
if (nbr_pending_connect(nbr)) |
618 |
|
|
event_del(&nbr->ev_connect); |
619 |
|
|
break; |
620 |
|
|
case NBR_STA_INITIAL: |
621 |
|
|
case NBR_STA_OPENREC: |
622 |
|
|
case NBR_STA_OPENSENT: |
623 |
|
|
case NBR_STA_OPER: |
624 |
|
|
log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); |
625 |
|
|
|
626 |
|
|
send_notification(nbr->tcp, status, msg_id, msg_type); |
627 |
|
|
|
628 |
|
|
nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); |
629 |
|
|
break; |
630 |
|
|
default: |
631 |
|
|
fatalx("session_shutdown: unknown neighbor state"); |
632 |
|
|
} |
633 |
|
|
} |
634 |
|
|
|
635 |
|
|
void |
636 |
|
|
session_close(struct nbr *nbr) |
637 |
|
|
{ |
638 |
|
|
log_debug("%s: closing session with lsr-id %s", __func__, |
639 |
|
|
inet_ntoa(nbr->id)); |
640 |
|
|
|
641 |
|
|
tcp_close(nbr->tcp); |
642 |
|
|
nbr_stop_ktimer(nbr); |
643 |
|
|
nbr_stop_ktimeout(nbr); |
644 |
|
|
nbr_stop_itimeout(nbr); |
645 |
|
|
} |
646 |
|
|
|
647 |
|
|
static ssize_t |
648 |
|
|
session_get_pdu(struct ibuf_read *r, char **b) |
649 |
|
|
{ |
650 |
|
|
struct ldp_hdr l; |
651 |
|
|
size_t av, dlen, left; |
652 |
|
|
|
653 |
|
|
av = r->wpos; |
654 |
|
|
if (av < sizeof(l)) |
655 |
|
|
return (0); |
656 |
|
|
|
657 |
|
|
memcpy(&l, r->buf, sizeof(l)); |
658 |
|
|
dlen = ntohs(l.length) + LDP_HDR_DEAD_LEN; |
659 |
|
|
if (dlen > av) |
660 |
|
|
return (0); |
661 |
|
|
|
662 |
|
|
if ((*b = malloc(dlen)) == NULL) |
663 |
|
|
return (-1); |
664 |
|
|
|
665 |
|
|
memcpy(*b, r->buf, dlen); |
666 |
|
|
if (dlen < av) { |
667 |
|
|
left = av - dlen; |
668 |
|
|
memmove(r->buf, r->buf + dlen, left); |
669 |
|
|
r->wpos = left; |
670 |
|
|
} else |
671 |
|
|
r->wpos = 0; |
672 |
|
|
|
673 |
|
|
return (dlen); |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
struct tcp_conn * |
677 |
|
|
tcp_new(int fd, struct nbr *nbr) |
678 |
|
|
{ |
679 |
|
|
struct tcp_conn *tcp; |
680 |
|
|
|
681 |
|
|
if ((tcp = calloc(1, sizeof(*tcp))) == NULL) |
682 |
|
|
fatal(__func__); |
683 |
|
|
|
684 |
|
|
tcp->fd = fd; |
685 |
|
|
evbuf_init(&tcp->wbuf, tcp->fd, session_write, tcp); |
686 |
|
|
|
687 |
|
|
if (nbr) { |
688 |
|
|
if ((tcp->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL) |
689 |
|
|
fatal(__func__); |
690 |
|
|
|
691 |
|
|
event_set(&tcp->rev, tcp->fd, EV_READ | EV_PERSIST, |
692 |
|
|
session_read, nbr); |
693 |
|
|
event_add(&tcp->rev, NULL); |
694 |
|
|
tcp->nbr = nbr; |
695 |
|
|
} |
696 |
|
|
|
697 |
|
|
return (tcp); |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
static void |
701 |
|
|
tcp_close(struct tcp_conn *tcp) |
702 |
|
|
{ |
703 |
|
|
/* try to flush write buffer */ |
704 |
|
|
msgbuf_write(&tcp->wbuf.wbuf); |
705 |
|
|
evbuf_clear(&tcp->wbuf); |
706 |
|
|
|
707 |
|
|
if (tcp->nbr) { |
708 |
|
|
event_del(&tcp->rev); |
709 |
|
|
free(tcp->rbuf); |
710 |
|
|
tcp->nbr->tcp = NULL; |
711 |
|
|
} |
712 |
|
|
|
713 |
|
|
close(tcp->fd); |
714 |
|
|
accept_unpause(); |
715 |
|
|
free(tcp); |
716 |
|
|
} |
717 |
|
|
|
718 |
|
|
static struct pending_conn * |
719 |
|
|
pending_conn_new(int fd, int af, union ldpd_addr *addr) |
720 |
|
|
{ |
721 |
|
|
struct pending_conn *pconn; |
722 |
|
|
struct timeval tv; |
723 |
|
|
|
724 |
|
|
if ((pconn = calloc(1, sizeof(*pconn))) == NULL) |
725 |
|
|
fatal(__func__); |
726 |
|
|
|
727 |
|
|
pconn->fd = fd; |
728 |
|
|
pconn->af = af; |
729 |
|
|
pconn->addr = *addr; |
730 |
|
|
evtimer_set(&pconn->ev_timeout, pending_conn_timeout, pconn); |
731 |
|
|
TAILQ_INSERT_TAIL(&global.pending_conns, pconn, entry); |
732 |
|
|
|
733 |
|
|
timerclear(&tv); |
734 |
|
|
tv.tv_sec = PENDING_CONN_TIMEOUT; |
735 |
|
|
if (evtimer_add(&pconn->ev_timeout, &tv) == -1) |
736 |
|
|
fatal(__func__); |
737 |
|
|
|
738 |
|
|
return (pconn); |
739 |
|
|
} |
740 |
|
|
|
741 |
|
|
void |
742 |
|
|
pending_conn_del(struct pending_conn *pconn) |
743 |
|
|
{ |
744 |
|
|
if (evtimer_pending(&pconn->ev_timeout, NULL) && |
745 |
|
|
evtimer_del(&pconn->ev_timeout) == -1) |
746 |
|
|
fatal(__func__); |
747 |
|
|
|
748 |
|
|
TAILQ_REMOVE(&global.pending_conns, pconn, entry); |
749 |
|
|
free(pconn); |
750 |
|
|
} |
751 |
|
|
|
752 |
|
|
struct pending_conn * |
753 |
|
|
pending_conn_find(int af, union ldpd_addr *addr) |
754 |
|
|
{ |
755 |
|
|
struct pending_conn *pconn; |
756 |
|
|
|
757 |
|
|
TAILQ_FOREACH(pconn, &global.pending_conns, entry) |
758 |
|
|
if (af == pconn->af && |
759 |
|
|
ldp_addrcmp(af, addr, &pconn->addr) == 0) |
760 |
|
|
return (pconn); |
761 |
|
|
|
762 |
|
|
return (NULL); |
763 |
|
|
} |
764 |
|
|
|
765 |
|
|
static void |
766 |
|
|
pending_conn_timeout(int fd, short event, void *arg) |
767 |
|
|
{ |
768 |
|
|
struct pending_conn *pconn = arg; |
769 |
|
|
struct tcp_conn *tcp; |
770 |
|
|
|
771 |
|
|
log_debug("%s: no adjacency with remote end: %s", __func__, |
772 |
|
|
log_addr(pconn->af, &pconn->addr)); |
773 |
|
|
|
774 |
|
|
/* |
775 |
|
|
* Create a write buffer detached from any neighbor to send a |
776 |
|
|
* notification message reliably. |
777 |
|
|
*/ |
778 |
|
|
tcp = tcp_new(pconn->fd, NULL); |
779 |
|
|
send_notification(tcp, S_NO_HELLO, 0, 0); |
780 |
|
|
msgbuf_write(&tcp->wbuf.wbuf); |
781 |
|
|
|
782 |
|
|
pending_conn_del(pconn); |
783 |
|
|
} |