]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/bgp/packets.c
pxlen works even in IPv6 mode.
[thirdparty/bird.git] / proto / bgp / packets.c
CommitLineData
c01e3741
MM
1/*
2 * BIRD -- BGP Packet Processing
3 *
4 * (c) 2000 Martin Mares <mj@ucw.cz>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
85368cd4 9#undef LOCAL_DEBUG
72a6ef11 10
c01e3741
MM
11#include "nest/bird.h"
12#include "nest/iface.h"
13#include "nest/protocol.h"
14#include "nest/route.h"
15#include "conf/conf.h"
72a6ef11
MM
16#include "lib/unaligned.h"
17#include "lib/socket.h"
c01e3741
MM
18
19#include "bgp.h"
72a6ef11
MM
20
21static byte *
22bgp_create_notification(struct bgp_conn *conn, byte *buf)
23{
85368cd4
MM
24 struct bgp_proto *p = conn->bgp;
25
26 BGP_TRACE(D_PACKETS, "Sending NOTIFICATION(code=%d.%d)", conn->notify_code, conn->notify_subcode);
72a6ef11
MM
27 buf[0] = conn->notify_code;
28 buf[1] = conn->notify_subcode;
efcece2d
MM
29 memcpy(buf+2, conn->notify_data, conn->notify_size);
30 return buf + 2 + conn->notify_size;
72a6ef11
MM
31}
32
33static byte *
34bgp_create_open(struct bgp_conn *conn, byte *buf)
35{
85368cd4
MM
36 struct bgp_proto *p = conn->bgp;
37
38 BGP_TRACE(D_PACKETS, "Sending OPEN(ver=%d,as=%d,hold=%d,id=%08x)",
39 BGP_VERSION, p->local_as, p->cf->hold_time, p->local_id);
72a6ef11 40 buf[0] = BGP_VERSION;
85368cd4
MM
41 put_u16(buf+1, p->local_as);
42 put_u16(buf+3, p->cf->hold_time);
43 put_u32(buf+5, p->local_id);
72a6ef11
MM
44 buf[9] = 0; /* No optional parameters */
45 return buf+10;
46}
47
f421cfdd
MM
48static unsigned int
49bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsigned int remains)
50{
51 byte *start = w;
52 ip_addr a;
53 int bytes;
54
55 while (!EMPTY_LIST(buck->prefixes) && remains >= 5)
56 {
57 struct bgp_prefix *px = SKIP_BACK(struct bgp_prefix, bucket_node, HEAD(buck->prefixes));
58 DBG("\tDequeued route %I/%d\n", px->n.prefix, px->n.pxlen);
59 *w++ = px->n.pxlen;
60 bytes = (px->n.pxlen + 7) / 8;
61 a = px->n.prefix;
62 ipa_hton(a);
63 memcpy(w, &a, bytes);
64 w += bytes;
65 rem_node(&px->bucket_node);
66 fib_delete(&p->prefix_fib, px);
67 }
68 return w - start;
69}
70
1c1da87b
MM
71#ifndef IPV6 /* IPv4 version */
72
72a6ef11
MM
73static byte *
74bgp_create_update(struct bgp_conn *conn, byte *buf)
75{
85368cd4 76 struct bgp_proto *p = conn->bgp;
f421cfdd
MM
77 struct bgp_bucket *buck;
78 int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4;
79 byte *w, *wold;
80 ip_addr ip;
81 int wd_size = 0;
82 int r_size = 0;
83
85368cd4 84 BGP_TRACE(D_PACKETS, "Sending UPDATE");
f421cfdd 85 w = buf+2;
85368cd4 86 if ((buck = p->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
f421cfdd
MM
87 {
88 DBG("Withdrawn routes:\n");
85368cd4 89 wd_size = bgp_encode_prefixes(p, w, buck, remains);
f421cfdd
MM
90 w += wd_size;
91 remains -= wd_size;
92 }
93 put_u16(buf, wd_size);
3fdbafb6 94
f421cfdd
MM
95 if (remains >= 2048)
96 {
85368cd4 97 while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next)
f421cfdd
MM
98 {
99 if (EMPTY_LIST(buck->prefixes))
100 {
101 DBG("Deleting empty bucket %p\n", buck);
102 rem_node(&buck->send_node);
85368cd4 103 bgp_free_bucket(p, buck);
f421cfdd
MM
104 continue;
105 }
106 DBG("Processing bucket %p\n", buck);
107 w = bgp_encode_attrs(w, buck);
108 remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - (w-buf);
85368cd4 109 r_size = bgp_encode_prefixes(p, w, buck, remains);
f421cfdd
MM
110 w += r_size;
111 break;
112 }
113 }
114 else
115 {
116 put_u16(w, 0);
117 w += 2;
118 }
119 return (wd_size || r_size) ? w : NULL;
72a6ef11
MM
120}
121
1c1da87b
MM
122#else /* IPv6 version */
123
124static byte *
125bgp_create_update(struct bgp_conn *conn, byte *buf)
126{
127 return NULL;
128}
129
130#endif
131
72a6ef11
MM
132static void
133bgp_create_header(byte *buf, unsigned int len, unsigned int type)
134{
135 memset(buf, 0xff, 16); /* Marker */
136 put_u16(buf+16, len);
137 buf[18] = type;
138}
139
3fdbafb6 140static int
72a6ef11
MM
141bgp_fire_tx(struct bgp_conn *conn)
142{
85368cd4 143 struct bgp_proto *p = conn->bgp;
72a6ef11
MM
144 unsigned int s = conn->packets_to_send;
145 sock *sk = conn->sk;
146 byte *buf = sk->tbuf;
147 byte *pkt = buf + BGP_HEADER_LENGTH;
148 byte *end;
149 int type;
150
151 if (s & (1 << PKT_SCHEDULE_CLOSE))
152 {
3fdbafb6
MM
153 bgp_close_conn(conn);
154 return 0;
72a6ef11
MM
155 }
156 if (s & (1 << PKT_NOTIFICATION))
157 {
158 s = 1 << PKT_SCHEDULE_CLOSE;
159 type = PKT_NOTIFICATION;
160 end = bgp_create_notification(conn, pkt);
161 }
162 else if (s & (1 << PKT_KEEPALIVE))
163 {
164 s &= ~(1 << PKT_KEEPALIVE);
165 type = PKT_KEEPALIVE;
166 end = pkt; /* Keepalives carry no data */
85368cd4 167 BGP_TRACE(D_PACKETS, "Sending KEEPALIVE");
3fdbafb6 168 bgp_start_timer(conn->keepalive_timer, conn->keepalive_time);
72a6ef11
MM
169 }
170 else if (s & (1 << PKT_OPEN))
171 {
172 s &= ~(1 << PKT_OPEN);
173 type = PKT_OPEN;
174 end = bgp_create_open(conn, pkt);
175 }
176 else if (s & (1 << PKT_UPDATE))
177 {
178 end = bgp_create_update(conn, pkt);
179 type = PKT_UPDATE;
180 if (!end)
181 {
182 conn->packets_to_send = 0;
3fdbafb6 183 return 0;
72a6ef11
MM
184 }
185 }
186 else
3fdbafb6 187 return 0;
72a6ef11
MM
188 conn->packets_to_send = s;
189 bgp_create_header(buf, end - buf, type);
3fdbafb6 190 return sk_send(sk, end - buf);
72a6ef11
MM
191}
192
193void
194bgp_schedule_packet(struct bgp_conn *conn, int type)
195{
196 DBG("BGP: Scheduling packet type %d\n", type);
197 conn->packets_to_send |= 1 << type;
3fdbafb6
MM
198 if (conn->sk->tpos == conn->sk->tbuf)
199 while (bgp_fire_tx(conn))
200 ;
72a6ef11
MM
201}
202
203void
204bgp_tx(sock *sk)
205{
206 struct bgp_conn *conn = sk->data;
207
208 DBG("BGP: TX hook\n");
3fdbafb6
MM
209 while (bgp_fire_tx(conn))
210 ;
211}
212
a47a0108
MM
213static int
214bgp_parse_options(struct bgp_conn *conn, byte *opt, int len)
215{
216 while (len > 0)
217 {
218 if (len < 2 || len < 2 + opt[1])
efcece2d 219 { bgp_error(conn, 2, 0, NULL, 0); return 0; }
a47a0108
MM
220#ifdef LOCAL_DEBUG
221 {
222 int i;
223 DBG("\tOption %02x:", opt[0]);
224 for(i=0; i<opt[1]; i++)
225 DBG(" %02x", opt[2+i]);
226 DBG("\n");
227 }
228#endif
229 switch (opt[0])
230 {
231 case 2:
232 /* Defined in draft-ietf-idr-bgp4-cap-neg-06 */
233 /* We can safely ignore all capabilities */
234 break;
235 default:
236 /*
237 * BGP specs don't tell us to send which option
238 * we didn't recognize, but it's common practice
239 * to do so. Also, capability negotiation with
240 * Cisco routers doesn't work without that.
241 */
efcece2d 242 bgp_error(conn, 2, 4, opt, opt[1]);
a47a0108
MM
243 return 0;
244 }
245 len -= 2 + opt[1];
246 opt += 2 + opt[1];
247 }
248 return 0;
249}
250
3fdbafb6
MM
251static void
252bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
253{
b552ecc4 254 struct bgp_conn *other;
3fdbafb6
MM
255 struct bgp_proto *p = conn->bgp;
256 struct bgp_config *cf = p->cf;
257 unsigned as, hold;
258 u32 id;
259
260 /* Check state */
261 if (conn->state != BS_OPENSENT)
efcece2d 262 { bgp_error(conn, 5, 0, NULL, 0); }
3fdbafb6
MM
263
264 /* Check message contents */
265 if (len < 29 || len != 29 + pkt[28])
efcece2d 266 { bgp_error(conn, 1, 2, pkt+16, 2); return; }
3fdbafb6 267 if (pkt[19] != BGP_VERSION)
efcece2d 268 { bgp_error(conn, 2, 1, pkt+19, 1); return; } /* RFC 1771 says 16 bits, draft-09 tells to use 8 */
3fdbafb6
MM
269 as = get_u16(pkt+20);
270 hold = get_u16(pkt+22);
271 id = get_u32(pkt+24);
85368cd4 272 BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", as, hold, id);
3fdbafb6 273 if (cf->remote_as && as != p->remote_as)
efcece2d 274 { bgp_error(conn, 2, 2, pkt+20, -2); return; }
3fdbafb6 275 if (hold > 0 && hold < 3)
efcece2d 276 { bgp_error(conn, 2, 6, pkt+22, 2); return; }
3fdbafb6 277 p->remote_id = id;
a47a0108
MM
278 if (bgp_parse_options(conn, pkt+29, pkt[28]))
279 return;
3fdbafb6 280 if (!id || id == 0xffffffff || id == p->local_id)
efcece2d 281 { bgp_error(conn, 2, 3, pkt+24, -4); return; }
3fdbafb6 282
b552ecc4
MM
283 /* Check the other connection */
284 other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
285 switch (other->state)
286 {
287 case BS_IDLE:
288 break;
289 case BS_CONNECT:
290 case BS_ACTIVE:
291 case BS_OPENSENT:
85368cd4 292 BGP_TRACE(D_EVENTS, "Connection collision, giving up the other connection");
b552ecc4
MM
293 bgp_close_conn(other);
294 break;
295 case BS_OPENCONFIRM:
296 if ((p->local_id < id) == (conn == &p->incoming_conn))
297 {
298 /* Should close the other connection */
85368cd4 299 BGP_TRACE(D_EVENTS, "Connection collision, giving up the other connection");
efcece2d 300 bgp_error(other, 6, 0, NULL, 0);
b552ecc4
MM
301 break;
302 }
303 /* Fall thru */
304 case BS_ESTABLISHED:
305 /* Should close this connection */
85368cd4 306 BGP_TRACE(D_EVENTS, "Connection collision, giving up this connection");
efcece2d 307 bgp_error(conn, 6, 0, NULL, 0);
b552ecc4
MM
308 return;
309 default:
310 bug("bgp_rx_open: Unknown state");
311 }
312
313 /* Make this connection primary */
314 conn->primary = 1;
315 p->conn = conn;
3fdbafb6
MM
316
317 /* Update our local variables */
318 if (hold < p->cf->hold_time)
319 conn->hold_time = hold;
320 else
321 conn->hold_time = p->cf->hold_time;
322 conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
323 p->remote_as = as;
324 p->remote_id = id;
325 DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x\n", conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id);
326
327 bgp_schedule_packet(conn, PKT_KEEPALIVE);
328 bgp_start_timer(conn->hold_timer, conn->hold_time);
329 conn->state = BS_OPENCONFIRM;
330}
331
973399ae
MM
332#define DECODE_PREFIX(pp, ll) do { \
333 int b = *pp++; \
334 int q; \
973399ae 335 ll--; \
1c1da87b 336 if (b > BITS_PER_IP_ADDRESS) { err=10; goto bad; } \
973399ae 337 q = (b+7) / 8; \
1c1da87b 338 if (ll < q) { err=1; goto bad; } \
c00d31be 339 memcpy(&prefix, pp, q); \
973399ae
MM
340 pp += q; \
341 ll -= q; \
f421cfdd
MM
342 ipa_ntoh(prefix); \
343 prefix = ipa_and(prefix, ipa_mkmask(b)); \
c00d31be 344 pxlen = b; \
973399ae
MM
345} while (0)
346
1c1da87b
MM
347static inline int
348bgp_get_nexthop(struct bgp_proto *bgp, rta *a)
349{
350 neighbor *neigh;
351 ip_addr nexthop;
352 struct eattr *nh = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
353 ASSERT(nh);
354 nexthop = *(ip_addr *) nh->u.ptr->data;
355 if (ipa_equal(nexthop, bgp->local_addr))
356 {
357 DBG("BGP: Loop!\n");
358 return 0;
359 }
360 neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh;
361 a->gw = neigh->addr;
362 a->iface = neigh->iface;
363 return 1;
364}
365
366#ifndef IPV6 /* IPv4 version */
367
3fdbafb6 368static void
1c1da87b
MM
369bgp_do_rx_update(struct bgp_conn *conn,
370 byte *withdrawn, int withdrawn_len,
371 byte *nlri, int nlri_len,
372 byte *attrs, int attr_len)
3fdbafb6 373{
85368cd4 374 struct bgp_proto *p = conn->bgp;
1c1da87b
MM
375 rta *a0;
376 rta *a = NULL;
c00d31be 377 ip_addr prefix;
c00d31be 378 net *n;
973399ae 379 rte e;
1c1da87b 380 int err = 0, pxlen;
973399ae
MM
381
382 /* Withdraw routes */
383 while (withdrawn_len)
384 {
385 DECODE_PREFIX(withdrawn, withdrawn_len);
c00d31be 386 DBG("Withdraw %I/%d\n", prefix, pxlen);
85368cd4
MM
387 if (n = net_find(p->p.table, prefix, pxlen))
388 rte_update(p->p.table, n, &p->p, NULL);
973399ae
MM
389 }
390
f94557de
MM
391 if (!attr_len && !nlri_len) /* shortcut */
392 return;
393
2a9e064d 394 a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri_len);
1c1da87b 395 if (a0 && nlri_len && bgp_get_nexthop(p, a0))
973399ae 396 {
2a9e064d 397 a = rta_lookup(a0);
c00d31be 398 while (nlri_len)
973399ae 399 {
c00d31be 400 rte *e;
f8809249 401 DECODE_PREFIX(nlri, nlri_len);
c00d31be 402 DBG("Add %I/%d\n", prefix, pxlen);
f8809249 403 e = rte_get_temp(rta_clone(a));
85368cd4 404 n = net_get(p->p.table, prefix, pxlen);
c00d31be
MM
405 e->net = n;
406 e->pflags = 0;
85368cd4 407 rte_update(p->p.table, n, &p->p, e);
973399ae 408 }
973399ae 409 }
1c1da87b
MM
410bad:
411 if (a)
412 rta_free(a);
413 if (err)
414 bgp_error(conn, 3, err, NULL, 0);
f8809249 415 return;
1c1da87b 416}
f8809249 417
1c1da87b
MM
418#else /* IPv6 version */
419
420#define DO_NLRI(name) \
421 start = x = p->name##_start; \
422 len = len0 = p->name##_len; \
423 if (len) \
424 { \
425 if (len < 3) goto bad; \
426 af = get_u16(x); \
427 sub = x[2]; \
428 x += 3; \
429 len -= 3; \
430 DBG("\tNLRI AF=%d sub=%d len=%d\n", af, sub, len);\
431 } \
432 else \
433 af = 0; \
434 if (af == BGP_AF_IPV6)
435
436static void
437bgp_do_rx_update(struct bgp_conn *conn,
438 byte *withdrawn, int withdrawn_len,
439 byte *nlri, int nlri_len,
440 byte *attrs, int attr_len)
441{
442 struct bgp_proto *p = conn->bgp;
443 byte *start, *x;
444 int len, len0;
445 unsigned af, sub;
446 rta *a0;
447 rta *a = NULL;
448 ip_addr prefix;
449 net *n;
450 rte e;
451 int err = 0, pxlen;
452
453 p->mp_reach_len = 0;
454 p->mp_unreach_len = 0;
455 a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, 0);
456 if (!a0)
457 return;
458
459 DO_NLRI(mp_unreach)
460 {
461 while (len)
462 {
463 DECODE_PREFIX(x, len);
464 DBG("Withdraw %I/%d\n", prefix, pxlen);
465 if (n = net_find(p->p.table, prefix, pxlen))
466 rte_update(p->p.table, n, &p->p, NULL);
467 }
468 }
469
470 DO_NLRI(mp_reach)
471 {
472 ea_list *e = lp_alloc(bgp_linpool, sizeof(ea_list) + sizeof(eattr));
473 struct adata *ad = lp_alloc(bgp_linpool, sizeof(struct adata) + 16);
474 int i;
475
476 /* Create fake NEXT_HOP attribute */
477 if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2)
478 goto bad;
479 e->next = a0->eattrs;
480 a0->eattrs = e;
481 e->flags = EALF_SORTED;
482 e->count = 1;
483 e->attrs[0].id = EA_CODE(EAP_BGP, BA_NEXT_HOP);
484 e->attrs[0].flags = BAF_TRANSITIVE;
485 e->attrs[0].type = EAF_TYPE_IP_ADDRESS;
486 e->attrs[0].u.ptr = ad;
487 ad->length = 16;
488 memcpy(ad->data, x+1, 16);
489 len -= *x + 1;
490 x += *x + 1;
491
492 /* Ignore SNPA info */
493 i = *x++;
494 while (i--)
495 {
496 if (len < 1 || len < 1 + *x)
497 goto bad;
498 len -= *x + 1;
499 x += *x + 1;
500 }
501
502 if (bgp_get_nexthop(p, a0))
503 {
504 a = rta_lookup(a0);
505 while (len)
506 {
507 rte *e;
508 DECODE_PREFIX(x, len);
509 DBG("Add %I/%d\n", prefix, pxlen);
510 e = rte_get_temp(rta_clone(a));
511 n = net_get(p->p.table, prefix, pxlen);
512 e->net = n;
513 e->pflags = 0;
514 rte_update(p->p.table, n, &p->p, e);
515 }
516 rta_free(a);
517 }
518 }
519
520 return;
521
522bad:
523 bgp_error(conn, 3, 9, start, len0);
f8809249
MM
524 if (a)
525 rta_free(a);
1c1da87b
MM
526 return;
527}
528
529#endif
530
531static void
532bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
533{
534 struct bgp_proto *p = conn->bgp;
535 byte *withdrawn, *attrs, *nlri;
536 int withdrawn_len, attr_len, nlri_len;
537
538 BGP_TRACE(D_PACKETS, "Got UPDATE");
539 if (conn->state != BS_ESTABLISHED)
540 { bgp_error(conn, 5, 0, NULL, 0); return; }
541 bgp_start_timer(conn->hold_timer, conn->hold_time);
542
543 /* Find parts of the packet and check sizes */
544 if (len < 23)
545 {
546 bgp_error(conn, 1, 2, pkt+16, 2);
547 return;
548 }
549 withdrawn = pkt + 21;
550 withdrawn_len = get_u16(pkt + 19);
551 if (withdrawn_len + 23 > len)
552 goto malformed;
553 attrs = withdrawn + withdrawn_len + 2;
554 attr_len = get_u16(attrs - 2);
555 if (withdrawn_len + attr_len + 23 > len)
556 goto malformed;
557 nlri = attrs + attr_len;
558 nlri_len = len - withdrawn_len - attr_len - 23;
559 if (!attr_len && nlri_len)
560 goto malformed;
561 DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len);
562
563 lp_flush(bgp_linpool);
564
565 bgp_do_rx_update(conn, withdrawn, withdrawn_len, nlri, nlri_len, attrs, attr_len);
566 return;
567
568malformed:
efcece2d
MM
569 bgp_error(conn, 3, 1, NULL, 0);
570}
571
572static struct {
573 byte major, minor;
574 byte *msg;
575} bgp_msg_table[] = {
576 { 1, 0, "Invalid message header" },
577 { 1, 1, "Connection not synchronized" },
578 { 1, 2, "Bad message length" },
579 { 1, 3, "Bad message type" },
580 { 2, 0, "Invalid OPEN message" },
581 { 2, 1, "Unsupported version number" },
582 { 2, 2, "Bad peer AS" },
583 { 2, 3, "Bad BGP identifier" },
584 { 2, 4, "Unsupported optional parameter" },
585 { 2, 5, "Authentication failure" },
586 { 2, 6, "Unacceptable hold time" },
587 { 2, 7, "Required capability missing" }, /* capability negotiation draft */
588 { 3, 0, "Invalid UPDATE message" },
589 { 3, 1, "Malformed attribute list" },
590 { 3, 2, "Unrecognized well-known attribute" },
591 { 3, 3, "Missing mandatory attribute" },
592 { 3, 4, "Invalid attribute flags" },
593 { 3, 5, "Invalid attribute length" },
594 { 3, 6, "Invalid ORIGIN attribute" },
595 { 3, 7, "AS routing loop" }, /* Deprecated */
596 { 3, 8, "Invalid NEXT_HOP attribute" },
597 { 3, 9, "Optional attribute error" },
598 { 3, 10, "Invalid network field" },
599 { 3, 11, "Malformed AS_PATH" },
600 { 4, 0, "Hold timer expired" },
601 { 5, 0, "Finite state machine error" },
602 { 6, 0, "Cease" }
603};
604
605void
606bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len)
607{
608 byte *name, namebuf[16];
609 byte *t, argbuf[36];
610 unsigned i;
611
85733143
MM
612 if (code == 6 && !subcode) /* Don't report Cease messages */
613 return;
614
efcece2d
MM
615 bsprintf(namebuf, "%d.%d", code, subcode);
616 name = namebuf;
617 for (i=0; i < ARRAY_SIZE(bgp_msg_table); i++)
618 if (bgp_msg_table[i].major == code && bgp_msg_table[i].minor == subcode)
619 {
620 name = bgp_msg_table[i].msg;
621 break;
622 }
623 t = argbuf;
624 if (len)
625 {
626 *t++ = ':';
627 *t++ = ' ';
628 if (len > 16)
629 len = 16;
630 for (i=0; i<len; i++)
631 t += bsprintf(t, "%02x", data[i]);
632 }
633 *t = 0;
634 log(L_REMOTE "%s: %s: %s%s", p->p.name, msg, name, argbuf);
3fdbafb6
MM
635}
636
637static void
638bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len)
639{
640 unsigned arg;
641
642 if (len < 21)
643 {
efcece2d 644 bgp_error(conn, 1, 2, pkt+16, 2);
3fdbafb6
MM
645 return;
646 }
efcece2d 647 bgp_log_error(conn->bgp, "Received error notification", pkt[19], pkt[20], pkt+21, len-21);
3fdbafb6 648 conn->error_flag = 1;
b552ecc4
MM
649 if (conn->primary)
650 proto_notify_state(&conn->bgp->p, PS_STOP);
3fdbafb6
MM
651 bgp_schedule_packet(conn, PKT_SCHEDULE_CLOSE);
652}
653
654static void
655bgp_rx_keepalive(struct bgp_conn *conn, byte *pkt, unsigned len)
656{
85368cd4
MM
657 struct bgp_proto *p = conn->bgp;
658
659 BGP_TRACE(D_PACKETS, "Got KEEPALIVE");
3fdbafb6
MM
660 bgp_start_timer(conn->hold_timer, conn->hold_time);
661 switch (conn->state)
662 {
663 case BS_OPENCONFIRM:
664 DBG("BGP: UP!!!\n");
665 conn->state = BS_ESTABLISHED;
ae8f5584 666 bgp_attr_init(conn->bgp);
3fdbafb6
MM
667 proto_notify_state(&conn->bgp->p, PS_UP);
668 break;
669 case BS_ESTABLISHED:
670 break;
671 default:
efcece2d 672 bgp_error(conn, 5, 0, NULL, 0);
3fdbafb6
MM
673 }
674}
675
676static void
677bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned len)
678{
679 DBG("BGP: Got packet %02x (%d bytes)\n", pkt[18], len);
680 switch (pkt[18])
681 {
682 case PKT_OPEN: return bgp_rx_open(conn, pkt, len);
683 case PKT_UPDATE: return bgp_rx_update(conn, pkt, len);
684 case PKT_NOTIFICATION: return bgp_rx_notification(conn, pkt, len);
685 case PKT_KEEPALIVE: return bgp_rx_keepalive(conn, pkt, len);
efcece2d 686 default: bgp_error(conn, 1, 3, pkt+18, 1);
3fdbafb6 687 }
72a6ef11
MM
688}
689
690int
691bgp_rx(sock *sk, int size)
692{
693 struct bgp_conn *conn = sk->data;
694 byte *pkt_start = sk->rbuf;
695 byte *end = pkt_start + size;
3fdbafb6 696 unsigned i, len;
72a6ef11
MM
697
698 DBG("BGP: RX hook: Got %d bytes\n", size);
699 while (end >= pkt_start + BGP_HEADER_LENGTH)
700 {
701 if (conn->error_flag)
3fdbafb6 702 {
efcece2d
MM
703 /*
704 * We still need to remember the erroneous packet, so that
705 * we can generate error notifications properly. To avoid
706 * subsequent reads rewriting the buffer, we just reset the
707 * rx_hook.
708 */
3fdbafb6 709 DBG("BGP: Error, dropping input\n");
efcece2d
MM
710 sk->rx_hook = NULL;
711 return 0;
3fdbafb6
MM
712 }
713 for(i=0; i<16; i++)
714 if (pkt_start[i] != 0xff)
715 {
efcece2d 716 bgp_error(conn, 1, 1, NULL, 0);
3fdbafb6
MM
717 break;
718 }
719 len = get_u16(pkt_start+16);
720 if (len < BGP_HEADER_LENGTH || len > BGP_MAX_PACKET_LENGTH)
721 {
efcece2d 722 bgp_error(conn, 1, 2, pkt_start+16, 2);
3fdbafb6
MM
723 break;
724 }
5f532add
MM
725 if (end < pkt_start + len)
726 break;
727 bgp_rx_packet(conn, pkt_start, len);
728 pkt_start += len;
72a6ef11
MM
729 }
730 if (pkt_start != sk->rbuf)
731 {
732 memmove(sk->rbuf, pkt_start, end - pkt_start);
733 sk->rpos = sk->rbuf + (end - pkt_start);
734 }
735 return 0;
736}