diff options
Diffstat (limited to 'drivers/net/ipvlan/ipvlan_core.c')
-rw-r--r-- | drivers/net/ipvlan/ipvlan_core.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index b5f9511d819e..b4e990743e1d 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c | |||
@@ -560,6 +560,7 @@ int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev) | |||
560 | case IPVLAN_MODE_L2: | 560 | case IPVLAN_MODE_L2: |
561 | return ipvlan_xmit_mode_l2(skb, dev); | 561 | return ipvlan_xmit_mode_l2(skb, dev); |
562 | case IPVLAN_MODE_L3: | 562 | case IPVLAN_MODE_L3: |
563 | case IPVLAN_MODE_L3S: | ||
563 | return ipvlan_xmit_mode_l3(skb, dev); | 564 | return ipvlan_xmit_mode_l3(skb, dev); |
564 | } | 565 | } |
565 | 566 | ||
@@ -664,6 +665,8 @@ rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb) | |||
664 | return ipvlan_handle_mode_l2(pskb, port); | 665 | return ipvlan_handle_mode_l2(pskb, port); |
665 | case IPVLAN_MODE_L3: | 666 | case IPVLAN_MODE_L3: |
666 | return ipvlan_handle_mode_l3(pskb, port); | 667 | return ipvlan_handle_mode_l3(pskb, port); |
668 | case IPVLAN_MODE_L3S: | ||
669 | return RX_HANDLER_PASS; | ||
667 | } | 670 | } |
668 | 671 | ||
669 | /* Should not reach here */ | 672 | /* Should not reach here */ |
@@ -672,3 +675,94 @@ rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb) | |||
672 | kfree_skb(skb); | 675 | kfree_skb(skb); |
673 | return RX_HANDLER_CONSUMED; | 676 | return RX_HANDLER_CONSUMED; |
674 | } | 677 | } |
678 | |||
679 | static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb, | ||
680 | struct net_device *dev) | ||
681 | { | ||
682 | struct ipvl_addr *addr = NULL; | ||
683 | struct ipvl_port *port; | ||
684 | void *lyr3h; | ||
685 | int addr_type; | ||
686 | |||
687 | if (!dev || !netif_is_ipvlan_port(dev)) | ||
688 | goto out; | ||
689 | |||
690 | port = ipvlan_port_get_rcu(dev); | ||
691 | if (!port || port->mode != IPVLAN_MODE_L3S) | ||
692 | goto out; | ||
693 | |||
694 | lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); | ||
695 | if (!lyr3h) | ||
696 | goto out; | ||
697 | |||
698 | addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true); | ||
699 | out: | ||
700 | return addr; | ||
701 | } | ||
702 | |||
703 | struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, struct sk_buff *skb, | ||
704 | u16 proto) | ||
705 | { | ||
706 | struct ipvl_addr *addr; | ||
707 | struct net_device *sdev; | ||
708 | |||
709 | addr = ipvlan_skb_to_addr(skb, dev); | ||
710 | if (!addr) | ||
711 | goto out; | ||
712 | |||
713 | sdev = addr->master->dev; | ||
714 | switch (proto) { | ||
715 | case AF_INET: | ||
716 | { | ||
717 | int err; | ||
718 | struct iphdr *ip4h = ip_hdr(skb); | ||
719 | |||
720 | err = ip_route_input_noref(skb, ip4h->daddr, ip4h->saddr, | ||
721 | ip4h->tos, sdev); | ||
722 | if (unlikely(err)) | ||
723 | goto out; | ||
724 | break; | ||
725 | } | ||
726 | case AF_INET6: | ||
727 | { | ||
728 | struct dst_entry *dst; | ||
729 | struct ipv6hdr *ip6h = ipv6_hdr(skb); | ||
730 | int flags = RT6_LOOKUP_F_HAS_SADDR; | ||
731 | struct flowi6 fl6 = { | ||
732 | .flowi6_iif = sdev->ifindex, | ||
733 | .daddr = ip6h->daddr, | ||
734 | .saddr = ip6h->saddr, | ||
735 | .flowlabel = ip6_flowinfo(ip6h), | ||
736 | .flowi6_mark = skb->mark, | ||
737 | .flowi6_proto = ip6h->nexthdr, | ||
738 | }; | ||
739 | |||
740 | skb_dst_drop(skb); | ||
741 | dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6, flags); | ||
742 | skb_dst_set(skb, dst); | ||
743 | break; | ||
744 | } | ||
745 | default: | ||
746 | break; | ||
747 | } | ||
748 | |||
749 | out: | ||
750 | return skb; | ||
751 | } | ||
752 | |||
753 | unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb, | ||
754 | const struct nf_hook_state *state) | ||
755 | { | ||
756 | struct ipvl_addr *addr; | ||
757 | unsigned int len; | ||
758 | |||
759 | addr = ipvlan_skb_to_addr(skb, skb->dev); | ||
760 | if (!addr) | ||
761 | goto out; | ||
762 | |||
763 | skb->dev = addr->master->dev; | ||
764 | len = skb->len + ETH_HLEN; | ||
765 | ipvlan_count_rx(addr->master, len, true, false); | ||
766 | out: | ||
767 | return NF_ACCEPT; | ||
768 | } | ||