Annotation of embedaddon/bird2/proto/radv/packets.c, revision 1.1
1.1 ! misho 1: /*
! 2: * BIRD -- RAdv Packet Processing
! 3: *
! 4: * (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org>
! 5: * (c) 2011--2019 CZ.NIC z.s.p.o.
! 6: *
! 7: * Can be freely distributed and used under the terms of the GNU GPL.
! 8: */
! 9:
! 10:
! 11: #include <stdlib.h>
! 12: #include "radv.h"
! 13:
! 14: struct radv_ra_packet
! 15: {
! 16: u8 type;
! 17: u8 code;
! 18: u16 checksum;
! 19: u8 current_hop_limit;
! 20: u8 flags;
! 21: u16 router_lifetime;
! 22: u32 reachable_time;
! 23: u32 retrans_timer;
! 24: };
! 25:
! 26: #define OPT_RA_MANAGED 0x80
! 27: #define OPT_RA_OTHER_CFG 0x40
! 28:
! 29: #define OPT_PREFIX 3
! 30: #define OPT_MTU 5
! 31: #define OPT_ROUTE 24
! 32: #define OPT_RDNSS 25
! 33: #define OPT_DNSSL 31
! 34:
! 35: struct radv_opt_prefix
! 36: {
! 37: u8 type;
! 38: u8 length;
! 39: u8 pxlen;
! 40: u8 flags;
! 41: u32 valid_lifetime;
! 42: u32 preferred_lifetime;
! 43: u32 reserved;
! 44: ip6_addr prefix;
! 45: };
! 46:
! 47: #define OPT_PX_ONLINK 0x80
! 48: #define OPT_PX_AUTONOMOUS 0x40
! 49:
! 50: struct radv_opt_mtu
! 51: {
! 52: u8 type;
! 53: u8 length;
! 54: u16 reserved;
! 55: u32 mtu;
! 56: };
! 57:
! 58: struct radv_opt_route {
! 59: u8 type;
! 60: u8 length;
! 61: u8 pxlen;
! 62: u8 flags;
! 63: u32 lifetime;
! 64: u8 prefix[];
! 65: };
! 66:
! 67: struct radv_opt_rdnss
! 68: {
! 69: u8 type;
! 70: u8 length;
! 71: u16 reserved;
! 72: u32 lifetime;
! 73: ip6_addr servers[];
! 74: };
! 75:
! 76: struct radv_opt_dnssl
! 77: {
! 78: u8 type;
! 79: u8 length;
! 80: u16 reserved;
! 81: u32 lifetime;
! 82: char domain[];
! 83: };
! 84:
! 85: static int
! 86: radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt,
! 87: char **buf, char *bufend)
! 88: {
! 89: struct radv_proto *p = ifa->ra;
! 90: u8 px_blocks = (net6_pxlen(rt->n.addr) + 63) / 64;
! 91: u8 opt_len = 8 * (1 + px_blocks);
! 92:
! 93: if (*buf + opt_len > bufend)
! 94: {
! 95: log(L_WARN, "%s: Too many RA options on interface %s",
! 96: p->p.name, ifa->iface->name);
! 97: return -1;
! 98: }
! 99:
! 100: uint preference = rt->preference_set ? rt->preference : ifa->cf->route_preference;
! 101: uint lifetime = rt->lifetime_set ? rt->lifetime : ifa->cf->route_lifetime;
! 102: uint valid = rt->valid && p->valid && (p->active || !ifa->cf->route_lifetime_sensitive);
! 103:
! 104: struct radv_opt_route *opt = (void *) *buf;
! 105: *buf += opt_len;
! 106: opt->type = OPT_ROUTE;
! 107: opt->length = 1 + px_blocks;
! 108: opt->pxlen = net6_pxlen(rt->n.addr);
! 109: opt->flags = preference;
! 110: opt->lifetime = valid ? htonl(lifetime) : 0;
! 111:
! 112: /* Copy the relevant part of the prefix */
! 113: ip6_addr px_addr = ip6_hton(net6_prefix(rt->n.addr));
! 114: memcpy(opt->prefix, &px_addr, 8 * px_blocks);
! 115:
! 116: /* Keeping track of first linger timeout */
! 117: if (!rt->valid)
! 118: ifa->valid_time = MIN(ifa->valid_time, rt->changed + ifa->cf->route_linger_time S);
! 119:
! 120: return 0;
! 121: }
! 122:
! 123: static int
! 124: radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend)
! 125: {
! 126: struct radv_rdnss_config *rcf = HEAD(*rdnss_list);
! 127:
! 128: while(NODE_VALID(rcf))
! 129: {
! 130: struct radv_rdnss_config *rcf_base = rcf;
! 131: struct radv_opt_rdnss *op = (void *) *buf;
! 132: int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip6_addr);
! 133: int i = 0;
! 134:
! 135: if (max_i < 1)
! 136: goto too_much;
! 137:
! 138: op->type = OPT_RDNSS;
! 139: op->reserved = 0;
! 140:
! 141: if (rcf->lifetime_mult)
! 142: op->lifetime = htonl(rcf->lifetime_mult * ifa->cf->max_ra_int);
! 143: else
! 144: op->lifetime = htonl(rcf->lifetime);
! 145:
! 146: while(NODE_VALID(rcf) &&
! 147: (rcf->lifetime == rcf_base->lifetime) &&
! 148: (rcf->lifetime_mult == rcf_base->lifetime_mult))
! 149: {
! 150: if (i >= max_i)
! 151: goto too_much;
! 152:
! 153: op->servers[i] = ip6_hton(rcf->server);
! 154: i++;
! 155:
! 156: rcf = NODE_NEXT(rcf);
! 157: }
! 158:
! 159: op->length = 1+2*i;
! 160: *buf += 8 * op->length;
! 161: }
! 162:
! 163: return 0;
! 164:
! 165: too_much:
! 166: log(L_WARN "%s: Too many RA options on interface %s",
! 167: ifa->ra->p.name, ifa->iface->name);
! 168: return -1;
! 169: }
! 170:
! 171: int
! 172: radv_process_domain(struct radv_dnssl_config *cf)
! 173: {
! 174: /* Format of domain in search list is <size> <label> <size> <label> ... 0 */
! 175:
! 176: char *dom = cf->domain;
! 177: char *dom_end = dom; /* Just to */
! 178: u8 *dlen_save = &cf->dlen_first;
! 179: uint len;
! 180:
! 181: while (dom_end)
! 182: {
! 183: dom_end = strchr(dom, '.');
! 184: len = dom_end ? (uint)(dom_end - dom) : strlen(dom);
! 185:
! 186: if (len < 1 || len > 63)
! 187: return -1;
! 188:
! 189: *dlen_save = len;
! 190: dlen_save = (u8 *) dom_end;
! 191:
! 192: dom += len + 1;
! 193: }
! 194:
! 195: len = dom - cf->domain;
! 196: if (len > 254)
! 197: return -1;
! 198:
! 199: cf->dlen_all = len;
! 200:
! 201: return 0;
! 202: }
! 203:
! 204: static int
! 205: radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *bufend)
! 206: {
! 207: struct radv_dnssl_config *dcf = HEAD(*dnssl_list);
! 208:
! 209: while(NODE_VALID(dcf))
! 210: {
! 211: struct radv_dnssl_config *dcf_base = dcf;
! 212: struct radv_opt_dnssl *op = (void *) *buf;
! 213: int bsize = bufend - *buf - sizeof(struct radv_opt_dnssl);
! 214: int bpos = 0;
! 215:
! 216: if (bsize < 0)
! 217: goto too_much;
! 218:
! 219: bsize = bsize & ~7; /* Round down to multiples of 8 */
! 220:
! 221: op->type = OPT_DNSSL;
! 222: op->reserved = 0;
! 223:
! 224: if (dcf->lifetime_mult)
! 225: op->lifetime = htonl(dcf->lifetime_mult * ifa->cf->max_ra_int);
! 226: else
! 227: op->lifetime = htonl(dcf->lifetime);
! 228:
! 229: while(NODE_VALID(dcf) &&
! 230: (dcf->lifetime == dcf_base->lifetime) &&
! 231: (dcf->lifetime_mult == dcf_base->lifetime_mult))
! 232: {
! 233: if (bpos + dcf->dlen_all + 1 > bsize)
! 234: goto too_much;
! 235:
! 236: op->domain[bpos++] = dcf->dlen_first;
! 237: memcpy(op->domain + bpos, dcf->domain, dcf->dlen_all);
! 238: bpos += dcf->dlen_all;
! 239:
! 240: dcf = NODE_NEXT(dcf);
! 241: }
! 242:
! 243: int blen = (bpos + 7) / 8;
! 244: bzero(op->domain + bpos, 8 * blen - bpos);
! 245: op->length = 1 + blen;
! 246: *buf += 8 * op->length;
! 247: }
! 248:
! 249: return 0;
! 250:
! 251: too_much:
! 252: log(L_WARN "%s: Too many RA options on interface %s",
! 253: ifa->ra->p.name, ifa->iface->name);
! 254: return -1;
! 255: }
! 256:
! 257: static int
! 258: radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *px,
! 259: char **buf, char *bufend)
! 260: {
! 261: struct radv_prefix_config *pc = px->cf;
! 262:
! 263: if (*buf + sizeof(struct radv_opt_prefix) > bufend)
! 264: {
! 265: log(L_WARN "%s: Too many prefixes on interface %s",
! 266: ifa->ra->p.name, ifa->iface->name);
! 267: return -1;
! 268: }
! 269:
! 270: struct radv_opt_prefix *op = (void *) *buf;
! 271: op->type = OPT_PREFIX;
! 272: op->length = 4;
! 273: op->pxlen = px->prefix.pxlen;
! 274: op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) |
! 275: (pc->autonomous ? OPT_PX_AUTONOMOUS : 0);
! 276: op->valid_lifetime = (ifa->ra->active || !pc->valid_lifetime_sensitive) ?
! 277: htonl(pc->valid_lifetime) : 0;
! 278: op->preferred_lifetime = (ifa->ra->active || !pc->preferred_lifetime_sensitive) ?
! 279: htonl(pc->preferred_lifetime) : 0;
! 280: op->reserved = 0;
! 281: op->prefix = ip6_hton(px->prefix.prefix);
! 282: *buf += sizeof(*op);
! 283:
! 284: /* Keeping track of first linger timeout */
! 285: if (!px->valid)
! 286: ifa->valid_time = MIN(ifa->valid_time, px->changed + ifa->cf->prefix_linger_time S);
! 287:
! 288: return 0;
! 289: }
! 290:
! 291: static void
! 292: radv_prepare_ra(struct radv_iface *ifa)
! 293: {
! 294: struct radv_proto *p = ifa->ra;
! 295: struct radv_config *cf = (struct radv_config *) (p->p.cf);
! 296: struct radv_iface_config *ic = ifa->cf;
! 297: btime now = current_time();
! 298:
! 299: char *buf = ifa->sk->tbuf;
! 300: char *bufstart = buf;
! 301: char *bufend = buf + ifa->sk->tbsize;
! 302:
! 303: struct radv_ra_packet *pkt = (void *) buf;
! 304: pkt->type = ICMPV6_RA;
! 305: pkt->code = 0;
! 306: pkt->checksum = 0;
! 307: pkt->current_hop_limit = ic->current_hop_limit;
! 308: pkt->router_lifetime = (p->valid && (p->active || !ic->default_lifetime_sensitive)) ?
! 309: htons(ic->default_lifetime) : 0;
! 310: pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) |
! 311: (ic->other_config ? OPT_RA_OTHER_CFG : 0) |
! 312: (pkt->router_lifetime ? ic->default_preference : 0);
! 313: pkt->reachable_time = htonl(ic->reachable_time);
! 314: pkt->retrans_timer = htonl(ic->retrans_timer);
! 315: buf += sizeof(*pkt);
! 316:
! 317: if (ic->link_mtu)
! 318: {
! 319: struct radv_opt_mtu *om = (void *) buf;
! 320: om->type = OPT_MTU;
! 321: om->length = 1;
! 322: om->reserved = 0;
! 323: om->mtu = htonl(ic->link_mtu);
! 324: buf += sizeof (*om);
! 325: }
! 326:
! 327: /* Keeping track of first linger timeout */
! 328: ifa->valid_time = TIME_INFINITY;
! 329:
! 330: struct radv_prefix *px;
! 331: WALK_LIST(px, ifa->prefixes)
! 332: {
! 333: /* Skip invalid prefixes that are past linger timeout but still not pruned */
! 334: if (!px->valid && ((px->changed + ic->prefix_linger_time S) <= now))
! 335: continue;
! 336:
! 337: if (radv_prepare_prefix(ifa, px, &buf, bufend) < 0)
! 338: goto done;
! 339: }
! 340:
! 341: if (! ic->rdnss_local)
! 342: if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0)
! 343: goto done;
! 344:
! 345: if (radv_prepare_rdnss(ifa, &ic->rdnss_list, &buf, bufend) < 0)
! 346: goto done;
! 347:
! 348: if (! ic->dnssl_local)
! 349: if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0)
! 350: goto done;
! 351:
! 352: if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0)
! 353: goto done;
! 354:
! 355: if (p->fib_up)
! 356: {
! 357: FIB_WALK(&p->routes, struct radv_route, rt)
! 358: {
! 359: /* Skip invalid routes that are past linger timeout but still not pruned */
! 360: if (!rt->valid && ((rt->changed + ic->route_linger_time S) <= now))
! 361: continue;
! 362:
! 363: if (radv_prepare_route(ifa, rt, &buf, bufend) < 0)
! 364: goto done;
! 365: }
! 366: FIB_WALK_END;
! 367: }
! 368:
! 369: done:
! 370: ifa->plen = buf - bufstart;
! 371: }
! 372:
! 373:
! 374: void
! 375: radv_send_ra(struct radv_iface *ifa, ip_addr to)
! 376: {
! 377: struct radv_proto *p = ifa->ra;
! 378:
! 379: /* TX queue is already full */
! 380: if (!sk_tx_buffer_empty(ifa->sk))
! 381: return;
! 382:
! 383: if (ifa->valid_time <= current_time())
! 384: radv_invalidate(ifa);
! 385:
! 386: /* We store prepared RA in tbuf */
! 387: if (!ifa->plen)
! 388: radv_prepare_ra(ifa);
! 389:
! 390: if (ipa_zero(to))
! 391: {
! 392: to = IP6_ALL_NODES;
! 393: RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name);
! 394: }
! 395: else
! 396: {
! 397: RADV_TRACE(D_PACKETS, "Sending RA to %I via %s", to, ifa->iface->name);
! 398: }
! 399:
! 400: int done = sk_send_to(ifa->sk, ifa->plen, to, 0);
! 401: if (!done)
! 402: log(L_WARN "%s: TX queue full on %s", p->p.name, ifa->iface->name);
! 403: }
! 404:
! 405:
! 406: static void
! 407: radv_receive_rs(struct radv_proto *p, struct radv_iface *ifa, ip_addr from)
! 408: {
! 409: RADV_TRACE(D_PACKETS, "Received RS from %I via %s",
! 410: from, ifa->iface->name);
! 411:
! 412: if (ifa->cf->solicited_ra_unicast && ipa_nonzero(from))
! 413: radv_send_ra(ifa, from);
! 414: else
! 415: radv_iface_notify(ifa, RA_EV_RS);
! 416: }
! 417:
! 418: static int
! 419: radv_rx_hook(sock *sk, uint size)
! 420: {
! 421: struct radv_iface *ifa = sk->data;
! 422: struct radv_proto *p = ifa->ra;
! 423:
! 424: /* We want just packets from sk->iface */
! 425: if (sk->lifindex != sk->iface->index)
! 426: return 1;
! 427:
! 428: if (ipa_equal(sk->faddr, sk->saddr))
! 429: return 1;
! 430:
! 431: if (size < 8)
! 432: return 1;
! 433:
! 434: byte *buf = sk->rbuf;
! 435:
! 436: if (buf[1] != 0)
! 437: return 1;
! 438:
! 439: /* Validation is a bit sloppy - Hop Limit is not checked and
! 440: length of options is ignored for RS and left to later for RA */
! 441:
! 442: switch (buf[0])
! 443: {
! 444: case ICMPV6_RS:
! 445: radv_receive_rs(p, ifa, sk->faddr);
! 446: return 1;
! 447:
! 448: case ICMPV6_RA:
! 449: RADV_TRACE(D_PACKETS, "Received RA from %I via %s",
! 450: sk->faddr, ifa->iface->name);
! 451: /* FIXME - there should be some checking of received RAs, but we just ignore them */
! 452: return 1;
! 453:
! 454: default:
! 455: return 1;
! 456: }
! 457: }
! 458:
! 459: static void
! 460: radv_tx_hook(sock *sk)
! 461: {
! 462: struct radv_iface *ifa = sk->data;
! 463: log(L_INFO "%s: TX queue ready on %s", ifa->ra->p.name, ifa->iface->name);
! 464:
! 465: /* Some RAs may be missed due to full TX queue */
! 466: radv_iface_notify(ifa, RA_EV_RS);
! 467: }
! 468:
! 469: static void
! 470: radv_err_hook(sock *sk, int err)
! 471: {
! 472: struct radv_iface *ifa = sk->data;
! 473: log(L_ERR "%s: Socket error on %s: %M", ifa->ra->p.name, ifa->iface->name, err);
! 474: }
! 475:
! 476: int
! 477: radv_sk_open(struct radv_iface *ifa)
! 478: {
! 479: sock *sk = sk_new(ifa->pool);
! 480: sk->type = SK_IP;
! 481: sk->subtype = SK_IPV6;
! 482: sk->dport = ICMPV6_PROTO;
! 483: sk->saddr = ifa->addr->ip;
! 484: sk->vrf = ifa->ra->p.vrf;
! 485:
! 486: sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */
! 487: sk->rx_hook = radv_rx_hook;
! 488: sk->tx_hook = radv_tx_hook;
! 489: sk->err_hook = radv_err_hook;
! 490: sk->iface = ifa->iface;
! 491: sk->rbsize = 1024; // bufsize(ifa);
! 492: sk->tbsize = 1024; // bufsize(ifa);
! 493: sk->data = ifa;
! 494: sk->flags = SKF_LADDR_RX;
! 495:
! 496: if (sk_open(sk) < 0)
! 497: goto err;
! 498:
! 499: /* We want listen just to ICMPv6 messages of type RS and RA */
! 500: if (sk_set_icmp6_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0)
! 501: goto err;
! 502:
! 503: if (sk_setup_multicast(sk) < 0)
! 504: goto err;
! 505:
! 506: if (sk_join_group(sk, IP6_ALL_ROUTERS) < 0)
! 507: goto err;
! 508:
! 509: ifa->sk = sk;
! 510: return 1;
! 511:
! 512: err:
! 513: sk_log_error(sk, ifa->ra->p.name);
! 514: rfree(sk);
! 515: return 0;
! 516: }
! 517:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>