Annotation of embedaddon/bird/proto/radv/packets.c, revision 1.1
1.1 ! misho 1: /*
! 2: * BIRD -- RAdv Packet Processing
! 3: *
! 4: *
! 5: * Can be freely distributed and used under the terms of the GNU GPL.
! 6: */
! 7:
! 8:
! 9: #include <stdlib.h>
! 10: #include "radv.h"
! 11:
! 12: struct radv_ra_packet
! 13: {
! 14: u8 type;
! 15: u8 code;
! 16: u16 checksum;
! 17: u8 current_hop_limit;
! 18: u8 flags;
! 19: u16 router_lifetime;
! 20: u32 reachable_time;
! 21: u32 retrans_timer;
! 22: };
! 23:
! 24: #define OPT_RA_MANAGED 0x80
! 25: #define OPT_RA_OTHER_CFG 0x40
! 26:
! 27: #define OPT_PREFIX 3
! 28: #define OPT_MTU 5
! 29: #define OPT_RDNSS 25
! 30: #define OPT_DNSSL 31
! 31:
! 32: struct radv_opt_prefix
! 33: {
! 34: u8 type;
! 35: u8 length;
! 36: u8 pxlen;
! 37: u8 flags;
! 38: u32 valid_lifetime;
! 39: u32 preferred_lifetime;
! 40: u32 reserved;
! 41: ip_addr prefix;
! 42: };
! 43:
! 44: #define OPT_PX_ONLINK 0x80
! 45: #define OPT_PX_AUTONOMOUS 0x40
! 46:
! 47: struct radv_opt_mtu
! 48: {
! 49: u8 type;
! 50: u8 length;
! 51: u16 reserved;
! 52: u32 mtu;
! 53: };
! 54:
! 55: struct radv_opt_rdnss
! 56: {
! 57: u8 type;
! 58: u8 length;
! 59: u16 reserved;
! 60: u32 lifetime;
! 61: ip_addr servers[];
! 62: };
! 63:
! 64: struct radv_opt_dnssl
! 65: {
! 66: u8 type;
! 67: u8 length;
! 68: u16 reserved;
! 69: u32 lifetime;
! 70: char domain[];
! 71: };
! 72:
! 73:
! 74: static struct radv_prefix_config default_prefix = {
! 75: .onlink = 1,
! 76: .autonomous = 1,
! 77: .valid_lifetime = DEFAULT_VALID_LIFETIME,
! 78: .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
! 79: };
! 80:
! 81:
! 82: static struct radv_prefix_config *
! 83: radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
! 84: {
! 85: struct proto *p = &ifa->ra->p;
! 86: struct radv_config *cf = (struct radv_config *) (p->cf);
! 87: struct radv_prefix_config *pc;
! 88:
! 89: if (a->scope <= SCOPE_LINK)
! 90: return NULL;
! 91:
! 92: WALK_LIST(pc, ifa->cf->pref_list)
! 93: if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen))
! 94: return pc;
! 95:
! 96: WALK_LIST(pc, cf->pref_list)
! 97: if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen))
! 98: return pc;
! 99:
! 100: return &default_prefix;
! 101: }
! 102:
! 103: static int
! 104: radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend)
! 105: {
! 106: struct radv_rdnss_config *rcf = HEAD(*rdnss_list);
! 107:
! 108: while(NODE_VALID(rcf))
! 109: {
! 110: struct radv_rdnss_config *rcf_base = rcf;
! 111: struct radv_opt_rdnss *op = (void *) *buf;
! 112: int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip_addr);
! 113: int i = 0;
! 114:
! 115: if (max_i < 1)
! 116: goto too_much;
! 117:
! 118: op->type = OPT_RDNSS;
! 119: op->reserved = 0;
! 120:
! 121: if (rcf->lifetime_mult)
! 122: op->lifetime = htonl(rcf->lifetime_mult * ifa->cf->max_ra_int);
! 123: else
! 124: op->lifetime = htonl(rcf->lifetime);
! 125:
! 126: while(NODE_VALID(rcf) &&
! 127: (rcf->lifetime == rcf_base->lifetime) &&
! 128: (rcf->lifetime_mult == rcf_base->lifetime_mult))
! 129: {
! 130: if (i >= max_i)
! 131: goto too_much;
! 132:
! 133: op->servers[i] = rcf->server;
! 134: ipa_hton(op->servers[i]);
! 135: i++;
! 136:
! 137: rcf = NODE_NEXT(rcf);
! 138: }
! 139:
! 140: op->length = 1+2*i;
! 141: *buf += 8 * op->length;
! 142: }
! 143:
! 144: return 0;
! 145:
! 146: too_much:
! 147: log(L_WARN "%s: Too many RA options on interface %s",
! 148: ifa->ra->p.name, ifa->iface->name);
! 149: return -1;
! 150: }
! 151:
! 152: int
! 153: radv_process_domain(struct radv_dnssl_config *cf)
! 154: {
! 155: /* Format of domain in search list is <size> <label> <size> <label> ... 0 */
! 156:
! 157: char *dom = cf->domain;
! 158: char *dom_end = dom; /* Just to */
! 159: u8 *dlen_save = &cf->dlen_first;
! 160: uint len;
! 161:
! 162: while (dom_end)
! 163: {
! 164: dom_end = strchr(dom, '.');
! 165: len = dom_end ? (uint)(dom_end - dom) : strlen(dom);
! 166:
! 167: if (len < 1 || len > 63)
! 168: return -1;
! 169:
! 170: *dlen_save = len;
! 171: dlen_save = (u8 *) dom_end;
! 172:
! 173: dom += len + 1;
! 174: }
! 175:
! 176: len = dom - cf->domain;
! 177: if (len > 254)
! 178: return -1;
! 179:
! 180: cf->dlen_all = len;
! 181:
! 182: return 0;
! 183: }
! 184:
! 185: static int
! 186: radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *bufend)
! 187: {
! 188: struct radv_dnssl_config *dcf = HEAD(*dnssl_list);
! 189:
! 190: while(NODE_VALID(dcf))
! 191: {
! 192: struct radv_dnssl_config *dcf_base = dcf;
! 193: struct radv_opt_dnssl *op = (void *) *buf;
! 194: int bsize = bufend - *buf - sizeof(struct radv_opt_dnssl);
! 195: int bpos = 0;
! 196:
! 197: if (bsize < 0)
! 198: goto too_much;
! 199:
! 200: bsize = bsize & ~7; /* Round down to multiples of 8 */
! 201:
! 202: op->type = OPT_DNSSL;
! 203: op->reserved = 0;
! 204:
! 205: if (dcf->lifetime_mult)
! 206: op->lifetime = htonl(dcf->lifetime_mult * ifa->cf->max_ra_int);
! 207: else
! 208: op->lifetime = htonl(dcf->lifetime);
! 209:
! 210: while(NODE_VALID(dcf) &&
! 211: (dcf->lifetime == dcf_base->lifetime) &&
! 212: (dcf->lifetime_mult == dcf_base->lifetime_mult))
! 213: {
! 214: if (bpos + dcf->dlen_all + 1 > bsize)
! 215: goto too_much;
! 216:
! 217: op->domain[bpos++] = dcf->dlen_first;
! 218: memcpy(op->domain + bpos, dcf->domain, dcf->dlen_all);
! 219: bpos += dcf->dlen_all;
! 220:
! 221: dcf = NODE_NEXT(dcf);
! 222: }
! 223:
! 224: int blen = (bpos + 7) / 8;
! 225: bzero(op->domain + bpos, 8 * blen - bpos);
! 226: op->length = 1 + blen;
! 227: *buf += 8 * op->length;
! 228: }
! 229:
! 230: return 0;
! 231:
! 232: too_much:
! 233: log(L_WARN "%s: Too many RA options on interface %s",
! 234: ifa->ra->p.name, ifa->iface->name);
! 235: return -1;
! 236: }
! 237:
! 238: static void
! 239: radv_prepare_ra(struct radv_iface *ifa)
! 240: {
! 241: struct proto_radv *ra = ifa->ra;
! 242: struct radv_config *cf = (struct radv_config *) (ra->p.cf);
! 243: struct radv_iface_config *ic = ifa->cf;
! 244:
! 245: char *buf = ifa->sk->tbuf;
! 246: char *bufstart = buf;
! 247: char *bufend = buf + ifa->sk->tbsize;
! 248:
! 249: struct radv_ra_packet *pkt = (void *) buf;
! 250: pkt->type = ICMPV6_RA;
! 251: pkt->code = 0;
! 252: pkt->checksum = 0;
! 253: pkt->current_hop_limit = ic->current_hop_limit;
! 254: pkt->router_lifetime = (ra->active || !ic->default_lifetime_sensitive) ?
! 255: htons(ic->default_lifetime) : 0;
! 256: pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) |
! 257: (ic->other_config ? OPT_RA_OTHER_CFG : 0) |
! 258: (pkt->router_lifetime ? ic->default_preference : 0);
! 259: pkt->reachable_time = htonl(ic->reachable_time);
! 260: pkt->retrans_timer = htonl(ic->retrans_timer);
! 261: buf += sizeof(*pkt);
! 262:
! 263: if (ic->link_mtu)
! 264: {
! 265: struct radv_opt_mtu *om = (void *) buf;
! 266: om->type = OPT_MTU;
! 267: om->length = 1;
! 268: om->reserved = 0;
! 269: om->mtu = htonl(ic->link_mtu);
! 270: buf += sizeof (*om);
! 271: }
! 272:
! 273: struct ifa *addr;
! 274: WALK_LIST(addr, ifa->iface->addrs)
! 275: {
! 276: struct radv_prefix_config *pc;
! 277: pc = radv_prefix_match(ifa, addr);
! 278:
! 279: if (!pc || pc->skip)
! 280: continue;
! 281:
! 282: if (buf + sizeof(struct radv_opt_prefix) > bufend)
! 283: {
! 284: log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name);
! 285: goto done;
! 286: }
! 287:
! 288: struct radv_opt_prefix *op = (void *) buf;
! 289: op->type = OPT_PREFIX;
! 290: op->length = 4;
! 291: op->pxlen = addr->pxlen;
! 292: op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) |
! 293: (pc->autonomous ? OPT_PX_AUTONOMOUS : 0);
! 294: op->valid_lifetime = (ra->active || !pc->valid_lifetime_sensitive) ?
! 295: htonl(pc->valid_lifetime) : 0;
! 296: op->preferred_lifetime = (ra->active || !pc->preferred_lifetime_sensitive) ?
! 297: htonl(pc->preferred_lifetime) : 0;
! 298: op->reserved = 0;
! 299: op->prefix = addr->prefix;
! 300: ipa_hton(op->prefix);
! 301: buf += sizeof(*op);
! 302: }
! 303:
! 304: if (! ic->rdnss_local)
! 305: if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0)
! 306: goto done;
! 307:
! 308: if (radv_prepare_rdnss(ifa, &ic->rdnss_list, &buf, bufend) < 0)
! 309: goto done;
! 310:
! 311: if (! ic->dnssl_local)
! 312: if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0)
! 313: goto done;
! 314:
! 315: if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0)
! 316: goto done;
! 317:
! 318: done:
! 319: ifa->plen = buf - bufstart;
! 320: }
! 321:
! 322:
! 323: void
! 324: radv_send_ra(struct radv_iface *ifa, int shutdown)
! 325: {
! 326: struct proto_radv *ra = ifa->ra;
! 327:
! 328: /* We store prepared RA in tbuf */
! 329: if (!ifa->plen)
! 330: radv_prepare_ra(ifa);
! 331:
! 332: if (shutdown)
! 333: {
! 334: /*
! 335: * Modify router lifetime to 0, it is not restored because we suppose that
! 336: * the iface will be removed. The preference value also has to be zeroed.
! 337: * (RFC 4191 2.2: If router lifetime is 0, the preference value must be 0.)
! 338: */
! 339:
! 340: struct radv_ra_packet *pkt = (void *) ifa->sk->tbuf;
! 341: pkt->router_lifetime = 0;
! 342: pkt->flags &= ~RA_PREF_MASK;
! 343: }
! 344:
! 345: RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name);
! 346: sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0);
! 347: }
! 348:
! 349:
! 350: static int
! 351: radv_rx_hook(sock *sk, uint size)
! 352: {
! 353: struct radv_iface *ifa = sk->data;
! 354: struct proto_radv *ra = ifa->ra;
! 355:
! 356: /* We want just packets from sk->iface */
! 357: if (sk->lifindex != sk->iface->index)
! 358: return 1;
! 359:
! 360: if (ipa_equal(sk->faddr, ifa->addr->ip))
! 361: return 1;
! 362:
! 363: if (size < 8)
! 364: return 1;
! 365:
! 366: byte *buf = sk->rbuf;
! 367:
! 368: if (buf[1] != 0)
! 369: return 1;
! 370:
! 371: /* Validation is a bit sloppy - Hop Limit is not checked and
! 372: length of options is ignored for RS and left to later for RA */
! 373:
! 374: switch (buf[0])
! 375: {
! 376: case ICMPV6_RS:
! 377: RADV_TRACE(D_PACKETS, "Received RS from %I via %s",
! 378: sk->faddr, ifa->iface->name);
! 379: radv_iface_notify(ifa, RA_EV_RS);
! 380: return 1;
! 381:
! 382: case ICMPV6_RA:
! 383: RADV_TRACE(D_PACKETS, "Received RA from %I via %s",
! 384: sk->faddr, ifa->iface->name);
! 385: /* FIXME - there should be some checking of received RAs, but we just ignore them */
! 386: return 1;
! 387:
! 388: default:
! 389: return 1;
! 390: }
! 391: }
! 392:
! 393: static void
! 394: radv_tx_hook(sock *sk)
! 395: {
! 396: struct radv_iface *ifa = sk->data;
! 397: log(L_WARN "%s: TX hook called", ifa->ra->p.name);
! 398: }
! 399:
! 400: static void
! 401: radv_err_hook(sock *sk, int err)
! 402: {
! 403: struct radv_iface *ifa = sk->data;
! 404: log(L_ERR "%s: Socket error on %s: %M", ifa->ra->p.name, ifa->iface->name, err);
! 405: }
! 406:
! 407: int
! 408: radv_sk_open(struct radv_iface *ifa)
! 409: {
! 410: sock *sk = sk_new(ifa->ra->p.pool);
! 411: sk->type = SK_IP;
! 412: sk->dport = ICMPV6_PROTO;
! 413: sk->saddr = ifa->addr->ip;
! 414:
! 415: sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */
! 416: sk->rx_hook = radv_rx_hook;
! 417: sk->tx_hook = radv_tx_hook;
! 418: sk->err_hook = radv_err_hook;
! 419: sk->iface = ifa->iface;
! 420: sk->rbsize = 1024; // bufsize(ifa);
! 421: sk->tbsize = 1024; // bufsize(ifa);
! 422: sk->data = ifa;
! 423: sk->flags = SKF_LADDR_RX;
! 424:
! 425: if (sk_open(sk) < 0)
! 426: goto err;
! 427:
! 428: /* We want listen just to ICMPv6 messages of type RS and RA */
! 429: if (sk_set_icmp6_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0)
! 430: goto err;
! 431:
! 432: if (sk_setup_multicast(sk) < 0)
! 433: goto err;
! 434:
! 435: if (sk_join_group(sk, IP6_ALL_ROUTERS) < 0)
! 436: goto err;
! 437:
! 438: ifa->sk = sk;
! 439: return 1;
! 440:
! 441: err:
! 442: sk_log_error(sk, ifa->ra->p.name);
! 443: rfree(sk);
! 444: return 0;
! 445: }
! 446:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>