From: Martin <martin.vargh...@nokia.com>

Signed-off-by: Martin Varghese <martinvargheseno...@gmail.com>

Signed-off-by: Martin Varghese <martinvargheseno...@gmail.com>
---
 Documentation/networking/bareudp.txt | 18 ++++++++
 drivers/net/bareudp.c                | 82 +++++++++++++++++++++++++++++++++---
 include/net/bareudp.h                |  1 +
 include/uapi/linux/if_link.h         |  1 +
 4 files changed, 95 insertions(+), 7 deletions(-)

diff --git a/Documentation/networking/bareudp.txt 
b/Documentation/networking/bareudp.txt
index d2530e2..4de1022 100644
--- a/Documentation/networking/bareudp.txt
+++ b/Documentation/networking/bareudp.txt
@@ -9,6 +9,15 @@ The Bareudp tunnel module provides a generic L3 encapsulation 
tunnelling
 support for tunnelling different L3 protocols like MPLS, IP, NSH etc. inside
 a UDP tunnel.
 
+Special Handling
+----------------
+The bareudp device supports special handling for MPLS & IP as they can have
+multiple ethertypes.
+MPLS procotcol can have ethertypes 0x8847 (unicast) & 0x8847 (multicast).
+IP proctocol can have ethertypes 0x0800 (v4) & 0x866 (v6).
+This special handling can be enabled only for ethertype 0x0800 & 0x88847 with a
+flag called extended mode.
+
 Usage
 ------
 
@@ -21,3 +30,12 @@ This creates a bareudp tunnel device which tunnels L3 
traffic with ethertype
 The device will listen on UDP port 6635 to receive traffic.
 
 b. ip link delete bareudp0
+
+2. Device creation with extended mode enabled
+
+There are two ways to create a bareudp device for MPLS & IP with extended mode
+enabled
+
+a. ip link add dev  bareudp0 type bareudp dstport 6635 ethertype 0x8847 
extmode 1
+
+b. ip link add dev  bareudp0 type bareudp dstport 6635 ethertype mpls
diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c
index 7e6813a..2a688da 100644
--- a/drivers/net/bareudp.c
+++ b/drivers/net/bareudp.c
@@ -48,6 +48,7 @@ struct bareudp_dev {
        struct net_device  *dev;        /* netdev for bareudp tunnel */
        __be16             ethertype;
        u16                sport_min;
+       bool               ext_mode;
        struct bareudp_conf conf;
        struct bareudp_sock __rcu *sock4; /* IPv4 socket for bareudp tunnel */
 #if IS_ENABLED(CONFIG_IPV6)
@@ -82,15 +83,64 @@ static int bareudp_udp_encap_recv(struct sock *sk, struct 
sk_buff *skb)
                goto drop;
 
        bareudp = bs->bareudp;
-       proto = bareudp->ethertype;
+       if (!bareudp)
+               goto drop;
+
+       if (bareudp->ethertype == htons(ETH_P_IP)) {
+               struct iphdr *iphdr;
+
+               iphdr = (struct iphdr *)(skb->data + BAREUDP_BASE_HLEN);
+               if (iphdr->version == 4) {
+                       proto = bareudp->ethertype;
+               } else if (bareudp->ext_mode && (iphdr->version == 6)) {
+                       proto = htons(ETH_P_IPV6);
+               } else {
+                       bareudp->dev->stats.rx_dropped++;
+                       goto drop;
+               }
+       } else if (bareudp->ethertype == htons(ETH_P_MPLS_UC)) {
+               struct iphdr *tunnel_hdr;
+
+               tunnel_hdr = (struct iphdr *)skb_network_header(skb);
+               if (tunnel_hdr->version == 4) {
+                       if (!ipv4_is_multicast(tunnel_hdr->daddr)) {
+                               proto = bareudp->ethertype;
+                       } else if (bareudp->ext_mode &&
+                                  ipv4_is_multicast(tunnel_hdr->daddr)) {
+                               proto = htons(ETH_P_MPLS_MC);
+                       } else {
+                               bareudp->dev->stats.rx_dropped++;
+                               goto drop;
+                       }
+               } else {
+                       int addr_type;
+                       struct ipv6hdr *tunnel_hdr_v6;
+
+                       tunnel_hdr_v6 = (struct ipv6hdr 
*)skb_network_header(skb);
+                       addr_type =
+                       ipv6_addr_type((struct in6_addr 
*)&tunnel_hdr_v6->daddr);
+                       if (!(addr_type & IPV6_ADDR_MULTICAST)) {
+                               proto = bareudp->ethertype;
+                       } else if (bareudp->ext_mode &&
+                                  (addr_type & IPV6_ADDR_MULTICAST)) {
+                               proto = htons(ETH_P_MPLS_MC);
+                       } else {
+                               bareudp->dev->stats.rx_dropped++;
+                               goto drop;
+                       }
+               }
+       } else {
+               proto = bareudp->ethertype;
+       }
 
        if (iptunnel_pull_header(skb, BAREUDP_BASE_HLEN,
-                                proto,
-                                !net_eq(bareudp->net,
-                                        dev_net(bareudp->dev)))) {
+                               proto,
+                               !net_eq(bareudp->net,
+                                       dev_net(bareudp->dev)))) {
                bareudp->dev->stats.rx_dropped++;
                goto drop;
        }
+
        tun_dst = udp_tun_rx_dst(skb, bareudp_get_sk_family(bs), TUNNEL_KEY,
                                 0, 0);
        if (!tun_dst) {
@@ -522,10 +572,13 @@ static netdev_tx_t bareudp_xmit(struct sk_buff *skb, 
struct net_device *dev)
        int err;
 
        if (skb->protocol != bareudp->ethertype) {
-               err = -EINVAL;
-               goto tx_error;
+               if (!bareudp->ext_mode ||
+                   (skb->protocol !=  htons(ETH_P_MPLS_MC) &&
+                    skb->protocol !=  htons(ETH_P_IPV6))) {
+                       err = -EINVAL;
+                       goto tx_error;
+               }
        }
-
        info = skb_tunnel_info(skb);
        if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
                err = -EINVAL;
@@ -630,6 +683,7 @@ static int bareudp_change_mtu(struct net_device *dev, int 
new_mtu)
        [IFLA_BAREUDP_PORT]                = { .type = NLA_U16 },
        [IFLA_BAREUDP_ETHERTYPE]           = { .type = NLA_U16 },
        [IFLA_BAREUDP_SRCPORT_MIN]         = { .type = NLA_U16 },
+       [IFLA_BAREUDP_EXTMODE]             = { .type = NLA_FLAG },
 };
 
 static int bareudp_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -712,9 +766,15 @@ static int bareudp_configure(struct net *net, struct 
net_device *dev,
        if (t)
                return -EBUSY;
 
+       if (conf->ext_mode &&
+           (conf->ethertype != htons(ETH_P_MPLS_UC) &&
+            conf->ethertype != htons(ETH_P_IP)))
+               return -EINVAL;
+
        bareudp->conf = *conf;
        bareudp->ethertype = conf->ethertype;
        bareudp->sport_min = conf->sport_min;
+       bareudp->ext_mode = conf->ext_mode;
        err = register_netdevice(dev);
        if (err)
                return err;
@@ -737,6 +797,11 @@ static int bareudp2info(struct nlattr *data[], struct 
bareudp_conf *conf)
        if (data[IFLA_BAREUDP_SRCPORT_MIN])
                conf->sport_min =  nla_get_u16(data[IFLA_BAREUDP_SRCPORT_MIN]);
 
+       if (data[IFLA_BAREUDP_EXTMODE])
+               conf->ext_mode = true;
+       else
+               conf->ext_mode = false;
+
        return 0;
 }
 
@@ -779,6 +844,7 @@ static size_t bareudp_get_size(const struct net_device *dev)
        return  nla_total_size(sizeof(__be16)) +  /* IFLA_BAREUDP_PORT */
                nla_total_size(sizeof(__be16)) +  /* IFLA_BAREUDP_ETHERTYPE */
                nla_total_size(sizeof(__u16))  +  /* IFLA_BAREUDP_SRCPORT_MIN */
+               nla_total_size(0)              +  /* IFLA_BAREUDP_EXTMODE */
                0;
 }
 
@@ -792,6 +858,8 @@ static int bareudp_fill_info(struct sk_buff *skb, const 
struct net_device *dev)
                goto nla_put_failure;
        if (nla_put_u16(skb, IFLA_BAREUDP_SRCPORT_MIN, bareudp->conf.sport_min))
                goto nla_put_failure;
+       if (bareudp->ext_mode && nla_put_flag(skb, IFLA_BAREUDP_EXTMODE))
+               goto nla_put_failure;
 
        return 0;
 
diff --git a/include/net/bareudp.h b/include/net/bareudp.h
index 513fae6..2c121d8 100644
--- a/include/net/bareudp.h
+++ b/include/net/bareudp.h
@@ -10,6 +10,7 @@ struct bareudp_conf {
        __be16 ethertype;
        __be16 port;
        u16 sport_min;
+       bool ext_mode;
 };
 
 struct net_device *bareudp_dev_create(struct net *net, const char *name,
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 012f7e8..2b91c872 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -586,6 +586,7 @@ enum {
        IFLA_BAREUDP_PORT,
        IFLA_BAREUDP_ETHERTYPE,
        IFLA_BAREUDP_SRCPORT_MIN,
+       IFLA_BAREUDP_EXTMODE,
        __IFLA_BAREUDP_MAX
 };
 
-- 
1.8.3.1

Reply via email to