diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2015-03-03 20:13:56 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-04 00:26:06 -0500 |
commit | 03c0566542f4c7a45ce3193f27cbf5700b506c18 (patch) | |
tree | 97389268330933037ffe3cd5e4184f4c6b870359 /net/mpls | |
parent | 966bae3349da22bb6286f6f3874c279177b8633f (diff) |
mpls: Netlink commands to add, remove, and dump routes
This change adds two new netlink routing attributes:
RTA_VIA and RTA_NEWDST.
RTA_VIA specifies the specifies the next machine to send a packet to
like RTA_GATEWAY. RTA_VIA differs from RTA_GATEWAY in that it
includes the address family of the address of the next machine to send
a packet to. Currently the MPLS code supports addresses in AF_INET,
AF_INET6 and AF_PACKET. For AF_INET and AF_INET6 the destination mac
address is acquired from the neighbour table. For AF_PACKET the
destination mac_address is specified in the netlink configuration.
I think raw destination mac address support with the family AF_PACKET
will prove useful. There is MPLS-TP which is defined to operate
on machines that do not support internet packets of any flavor. Further
seem to be corner cases where it can be useful. At this point
I don't care much either way.
RTA_NEWDST specifies the destination address to forward the packet
with. MPLS typically changes it's destination address at every hop.
For a swap operation RTA_NEWDST is specified with a length of one label.
For a push operation RTA_NEWDST is specified with two or more labels.
For a pop operation RTA_NEWDST is not specified or equivalently an emtpy
RTAN_NEWDST is specified.
Those new netlink attributes are used to implement handling of rt-netlink
RTM_NEWROUTE, RTM_DELROUTE, and RTM_GETROUTE messages, to maintain the
MPLS label table.
rtm_to_route_config parses a netlink RTM_NEWROUTE or RTM_DELROUTE message,
verify no unhandled attributes or unhandled values are present and sets
up the data structures for mpls_route_add and mpls_route_del.
I did my best to match up with the existing conventions with the caveats
that MPLS addresses are all destination-specific-addresses, and so
don't properly have a scope.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mpls')
-rw-r--r-- | net/mpls/af_mpls.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 2d6612a10e30..b4d7cec398d2 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c | |||
@@ -212,6 +212,11 @@ static struct packet_type mpls_packet_type __read_mostly = { | |||
212 | .func = mpls_forward, | 212 | .func = mpls_forward, |
213 | }; | 213 | }; |
214 | 214 | ||
215 | const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { | ||
216 | [RTA_DST] = { .type = NLA_U32 }, | ||
217 | [RTA_OIF] = { .type = NLA_U32 }, | ||
218 | }; | ||
219 | |||
215 | struct mpls_route_config { | 220 | struct mpls_route_config { |
216 | u32 rc_protocol; | 221 | u32 rc_protocol; |
217 | u32 rc_ifindex; | 222 | u32 rc_ifindex; |
@@ -410,6 +415,22 @@ static struct notifier_block mpls_dev_notifier = { | |||
410 | .notifier_call = mpls_dev_notify, | 415 | .notifier_call = mpls_dev_notify, |
411 | }; | 416 | }; |
412 | 417 | ||
418 | static int nla_put_via(struct sk_buff *skb, | ||
419 | u16 family, const void *addr, int alen) | ||
420 | { | ||
421 | struct nlattr *nla; | ||
422 | struct rtvia *via; | ||
423 | |||
424 | nla = nla_reserve(skb, RTA_VIA, alen + 2); | ||
425 | if (!nla) | ||
426 | return -EMSGSIZE; | ||
427 | |||
428 | via = nla_data(nla); | ||
429 | via->rtvia_family = family; | ||
430 | memcpy(via->rtvia_addr, addr, alen); | ||
431 | return 0; | ||
432 | } | ||
433 | |||
413 | int nla_put_labels(struct sk_buff *skb, int attrtype, | 434 | int nla_put_labels(struct sk_buff *skb, int attrtype, |
414 | u8 labels, const u32 label[]) | 435 | u8 labels, const u32 label[]) |
415 | { | 436 | { |
@@ -467,6 +488,210 @@ int nla_get_labels(const struct nlattr *nla, | |||
467 | return 0; | 488 | return 0; |
468 | } | 489 | } |
469 | 490 | ||
491 | static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, | ||
492 | struct mpls_route_config *cfg) | ||
493 | { | ||
494 | struct rtmsg *rtm; | ||
495 | struct nlattr *tb[RTA_MAX+1]; | ||
496 | int index; | ||
497 | int err; | ||
498 | |||
499 | err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_mpls_policy); | ||
500 | if (err < 0) | ||
501 | goto errout; | ||
502 | |||
503 | err = -EINVAL; | ||
504 | rtm = nlmsg_data(nlh); | ||
505 | memset(cfg, 0, sizeof(*cfg)); | ||
506 | |||
507 | if (rtm->rtm_family != AF_MPLS) | ||
508 | goto errout; | ||
509 | if (rtm->rtm_dst_len != 20) | ||
510 | goto errout; | ||
511 | if (rtm->rtm_src_len != 0) | ||
512 | goto errout; | ||
513 | if (rtm->rtm_tos != 0) | ||
514 | goto errout; | ||
515 | if (rtm->rtm_table != RT_TABLE_MAIN) | ||
516 | goto errout; | ||
517 | /* Any value is acceptable for rtm_protocol */ | ||
518 | |||
519 | /* As mpls uses destination specific addresses | ||
520 | * (or source specific address in the case of multicast) | ||
521 | * all addresses have universal scope. | ||
522 | */ | ||
523 | if (rtm->rtm_scope != RT_SCOPE_UNIVERSE) | ||
524 | goto errout; | ||
525 | if (rtm->rtm_type != RTN_UNICAST) | ||
526 | goto errout; | ||
527 | if (rtm->rtm_flags != 0) | ||
528 | goto errout; | ||
529 | |||
530 | cfg->rc_label = LABEL_NOT_SPECIFIED; | ||
531 | cfg->rc_protocol = rtm->rtm_protocol; | ||
532 | cfg->rc_nlflags = nlh->nlmsg_flags; | ||
533 | cfg->rc_nlinfo.portid = NETLINK_CB(skb).portid; | ||
534 | cfg->rc_nlinfo.nlh = nlh; | ||
535 | cfg->rc_nlinfo.nl_net = sock_net(skb->sk); | ||
536 | |||
537 | for (index = 0; index <= RTA_MAX; index++) { | ||
538 | struct nlattr *nla = tb[index]; | ||
539 | if (!nla) | ||
540 | continue; | ||
541 | |||
542 | switch(index) { | ||
543 | case RTA_OIF: | ||
544 | cfg->rc_ifindex = nla_get_u32(nla); | ||
545 | break; | ||
546 | case RTA_NEWDST: | ||
547 | if (nla_get_labels(nla, MAX_NEW_LABELS, | ||
548 | &cfg->rc_output_labels, | ||
549 | cfg->rc_output_label)) | ||
550 | goto errout; | ||
551 | break; | ||
552 | case RTA_DST: | ||
553 | { | ||
554 | u32 label_count; | ||
555 | if (nla_get_labels(nla, 1, &label_count, | ||
556 | &cfg->rc_label)) | ||
557 | goto errout; | ||
558 | |||
559 | /* The first 16 labels are reserved, and may not be set */ | ||
560 | if (cfg->rc_label < 16) | ||
561 | goto errout; | ||
562 | |||
563 | break; | ||
564 | } | ||
565 | case RTA_VIA: | ||
566 | { | ||
567 | struct rtvia *via = nla_data(nla); | ||
568 | cfg->rc_via_family = via->rtvia_family; | ||
569 | cfg->rc_via_alen = nla_len(nla) - 2; | ||
570 | if (cfg->rc_via_alen > MAX_VIA_ALEN) | ||
571 | goto errout; | ||
572 | |||
573 | /* Validate the address family */ | ||
574 | switch(cfg->rc_via_family) { | ||
575 | case AF_PACKET: | ||
576 | break; | ||
577 | case AF_INET: | ||
578 | if (cfg->rc_via_alen != 4) | ||
579 | goto errout; | ||
580 | break; | ||
581 | case AF_INET6: | ||
582 | if (cfg->rc_via_alen != 16) | ||
583 | goto errout; | ||
584 | break; | ||
585 | default: | ||
586 | /* Unsupported address family */ | ||
587 | goto errout; | ||
588 | } | ||
589 | |||
590 | memcpy(cfg->rc_via, via->rtvia_addr, cfg->rc_via_alen); | ||
591 | break; | ||
592 | } | ||
593 | default: | ||
594 | /* Unsupported attribute */ | ||
595 | goto errout; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | err = 0; | ||
600 | errout: | ||
601 | return err; | ||
602 | } | ||
603 | |||
604 | static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) | ||
605 | { | ||
606 | struct mpls_route_config cfg; | ||
607 | int err; | ||
608 | |||
609 | err = rtm_to_route_config(skb, nlh, &cfg); | ||
610 | if (err < 0) | ||
611 | return err; | ||
612 | |||
613 | return mpls_route_del(&cfg); | ||
614 | } | ||
615 | |||
616 | |||
617 | static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) | ||
618 | { | ||
619 | struct mpls_route_config cfg; | ||
620 | int err; | ||
621 | |||
622 | err = rtm_to_route_config(skb, nlh, &cfg); | ||
623 | if (err < 0) | ||
624 | return err; | ||
625 | |||
626 | return mpls_route_add(&cfg); | ||
627 | } | ||
628 | |||
629 | static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, | ||
630 | u32 label, struct mpls_route *rt, int flags) | ||
631 | { | ||
632 | struct nlmsghdr *nlh; | ||
633 | struct rtmsg *rtm; | ||
634 | |||
635 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags); | ||
636 | if (nlh == NULL) | ||
637 | return -EMSGSIZE; | ||
638 | |||
639 | rtm = nlmsg_data(nlh); | ||
640 | rtm->rtm_family = AF_MPLS; | ||
641 | rtm->rtm_dst_len = 20; | ||
642 | rtm->rtm_src_len = 0; | ||
643 | rtm->rtm_tos = 0; | ||
644 | rtm->rtm_table = RT_TABLE_MAIN; | ||
645 | rtm->rtm_protocol = rt->rt_protocol; | ||
646 | rtm->rtm_scope = RT_SCOPE_UNIVERSE; | ||
647 | rtm->rtm_type = RTN_UNICAST; | ||
648 | rtm->rtm_flags = 0; | ||
649 | |||
650 | if (rt->rt_labels && | ||
651 | nla_put_labels(skb, RTA_NEWDST, rt->rt_labels, rt->rt_label)) | ||
652 | goto nla_put_failure; | ||
653 | if (nla_put_via(skb, rt->rt_via_family, rt->rt_via, rt->rt_via_alen)) | ||
654 | goto nla_put_failure; | ||
655 | if (rt->rt_dev && nla_put_u32(skb, RTA_OIF, rt->rt_dev->ifindex)) | ||
656 | goto nla_put_failure; | ||
657 | if (nla_put_labels(skb, RTA_DST, 1, &label)) | ||
658 | goto nla_put_failure; | ||
659 | |||
660 | nlmsg_end(skb, nlh); | ||
661 | return 0; | ||
662 | |||
663 | nla_put_failure: | ||
664 | nlmsg_cancel(skb, nlh); | ||
665 | return -EMSGSIZE; | ||
666 | } | ||
667 | |||
668 | static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) | ||
669 | { | ||
670 | struct net *net = sock_net(skb->sk); | ||
671 | unsigned int index; | ||
672 | |||
673 | ASSERT_RTNL(); | ||
674 | |||
675 | index = cb->args[0]; | ||
676 | if (index < 16) | ||
677 | index = 16; | ||
678 | |||
679 | for (; index < net->mpls.platform_labels; index++) { | ||
680 | struct mpls_route *rt; | ||
681 | rt = net->mpls.platform_label[index]; | ||
682 | if (!rt) | ||
683 | continue; | ||
684 | |||
685 | if (mpls_dump_route(skb, NETLINK_CB(cb->skb).portid, | ||
686 | cb->nlh->nlmsg_seq, RTM_NEWROUTE, | ||
687 | index, rt, NLM_F_MULTI) < 0) | ||
688 | break; | ||
689 | } | ||
690 | cb->args[0] = index; | ||
691 | |||
692 | return skb->len; | ||
693 | } | ||
694 | |||
470 | static int resize_platform_label_table(struct net *net, size_t limit) | 695 | static int resize_platform_label_table(struct net *net, size_t limit) |
471 | { | 696 | { |
472 | size_t size = sizeof(struct mpls_route *) * limit; | 697 | size_t size = sizeof(struct mpls_route *) * limit; |
@@ -662,6 +887,9 @@ static int __init mpls_init(void) | |||
662 | 887 | ||
663 | dev_add_pack(&mpls_packet_type); | 888 | dev_add_pack(&mpls_packet_type); |
664 | 889 | ||
890 | rtnl_register(PF_MPLS, RTM_NEWROUTE, mpls_rtm_newroute, NULL, NULL); | ||
891 | rtnl_register(PF_MPLS, RTM_DELROUTE, mpls_rtm_delroute, NULL, NULL); | ||
892 | rtnl_register(PF_MPLS, RTM_GETROUTE, NULL, mpls_dump_routes, NULL); | ||
665 | err = 0; | 893 | err = 0; |
666 | out: | 894 | out: |
667 | return err; | 895 | return err; |
@@ -674,6 +902,7 @@ module_init(mpls_init); | |||
674 | 902 | ||
675 | static void __exit mpls_exit(void) | 903 | static void __exit mpls_exit(void) |
676 | { | 904 | { |
905 | rtnl_unregister_all(PF_MPLS); | ||
677 | dev_remove_pack(&mpls_packet_type); | 906 | dev_remove_pack(&mpls_packet_type); |
678 | unregister_netdevice_notifier(&mpls_dev_notifier); | 907 | unregister_netdevice_notifier(&mpls_dev_notifier); |
679 | unregister_pernet_subsys(&mpls_net_ops); | 908 | unregister_pernet_subsys(&mpls_net_ops); |