summaryrefslogtreecommitdiff
path: root/drivers/net/bareudp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/bareudp.c')
-rw-r--r--drivers/net/bareudp.c67
1 files changed, 64 insertions, 3 deletions
diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c
index 3251896..77e7247 100644
--- a/drivers/net/bareudp.c
+++ b/drivers/net/bareudp.c
@@ -45,6 +45,7 @@ struct bareudp_dev {
__be16 ethertype;
__be16 port;
u16 sport_min;
+ bool multi_proto_mode;
struct socket __rcu *sock;
struct list_head next; /* bareudp node on namespace list */
struct gro_cells gro_cells;
@@ -70,7 +71,52 @@ static int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
else
family = AF_INET6;
- proto = bareudp->ethertype;
+ 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->multi_proto_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->multi_proto_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->multi_proto_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,
@@ -369,8 +415,12 @@ 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->multi_proto_mode ||
+ (skb->protocol != htons(ETH_P_MPLS_MC) &&
+ skb->protocol != htons(ETH_P_IPV6))) {
+ err = -EINVAL;
+ goto tx_error;
+ }
}
info = skb_tunnel_info(skb);
@@ -463,6 +513,7 @@ static const struct nla_policy bareudp_policy[IFLA_BAREUDP_MAX + 1] = {
[IFLA_BAREUDP_PORT] = { .type = NLA_U16 },
[IFLA_BAREUDP_ETHERTYPE] = { .type = NLA_U16 },
[IFLA_BAREUDP_SRCPORT_MIN] = { .type = NLA_U16 },
+ [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NLA_FLAG },
};
/* Info for udev, that this is a virtual tunnel endpoint */
@@ -545,9 +596,15 @@ static int bareudp_configure(struct net *net, struct net_device *dev,
if (t)
return -EBUSY;
+ if (conf->multi_proto_mode &&
+ (conf->ethertype != htons(ETH_P_MPLS_UC) &&
+ conf->ethertype != htons(ETH_P_IP)))
+ return -EINVAL;
+
bareudp->port = conf->port;
bareudp->ethertype = conf->ethertype;
bareudp->sport_min = conf->sport_min;
+ bareudp->multi_proto_mode = conf->multi_proto_mode;
err = register_netdevice(dev);
if (err)
return err;
@@ -604,6 +661,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_MULTIPROTO_MODE */
0;
}
@@ -617,6 +675,9 @@ 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->sport_min))
goto nla_put_failure;
+ if (bareudp->multi_proto_mode &&
+ nla_put_flag(skb, IFLA_BAREUDP_MULTIPROTO_MODE))
+ goto nla_put_failure;
return 0;