* [PATCH v1 net-next] ip6_tunnel: Adding support of mapping rules for MAP-E tunnel
From: Felix Jia @ 2018-11-20 1:53 UTC (permalink / raw)
To: David S . Miller
Cc: Blair Steven, netdev, Felix Jia, Sheena Mira-ato, Masakazu Asama
From: Blair Steven <blair.steven@alliedtelesis.co.nz>
Mapping of Addresses and Ports with Encapsulation (MAP-E) is defined in
RFC7597, and is an IPv6 transition technology providing interoperability
between IPv4 and IPv6 networks.
MAP-E uses the encapsulation mode described in RFC2473 (IPv6 Tunneling)
to transport IPv4 and IPv6 packets over an IPv6 network. It requires a
list rules for mapping between IPv4 prefix/shared addresses and IPv6
addresses.
This patch also support the mapping rules define in the draft3 version of
the RFC.
Co-developed-by: Felix Jia <felix.jia@alliedtelesis.co.nz>
Co-developed-by: Sheena Mira-ato <sheena.mira-ato@alliedtelesis.co.nz>
Co-developed-by: Masakazu Asama <masakazu.asama@gmail.com>
Signed-off-by: Blair Steven <blair.steven@alliedtelesis.co.nz>
Signed-off-by: Felix Jia <felix.jia@alliedtelesis.co.nz>
Signed-off-by: Sheena Mira-ato <sheena.mira-ato@alliedtelesis.co.nz>
Signed-off-by: Masakazu Asama <masakazu.asama@gmail.com>
---
include/net/ip6_tunnel.h | 18 ++
include/uapi/linux/if_tunnel.h | 18 ++
net/ipv6/ip6_tunnel.c | 491 ++++++++++++++++++++++++++++++++-
3 files changed, 525 insertions(+), 2 deletions(-)
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index 69b4bcf880c9..daa159185228 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -18,6 +18,16 @@
/* determine capability on a per-packet basis */
#define IP6_TNL_F_CAP_PER_PACKET 0x40000
+struct ip6_tnl_rule {
+ u8 version;
+ struct in6_addr ipv6_subnet;
+ u8 ipv6_prefixlen;
+ struct in_addr ipv4_subnet;
+ u8 ipv4_prefixlen;
+ u8 ea_length;
+ u8 psid_offset;
+};
+
struct __ip6_tnl_parm {
char name[IFNAMSIZ]; /* name of tunnel device */
int link; /* ifindex of underlying L2 interface */
@@ -40,6 +50,13 @@ struct __ip6_tnl_parm {
__u8 erspan_ver; /* ERSPAN version */
__u8 dir; /* direction */
__u16 hwid; /* hwid */
+ __u8 rule_action;
+ struct ip6_tnl_rule rule;
+};
+
+struct ip6_rule_list {
+ struct list_head list;
+ struct ip6_tnl_rule data;
};
/* IPv6 tunnel */
@@ -63,6 +80,7 @@ struct ip6_tnl {
int encap_hlen; /* Encap header length (FOU,GUE) */
struct ip_tunnel_encap encap;
int mlink;
+ struct ip6_rule_list rules;
};
struct ip6_tnl_encap_ops {
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
index 1b3d148c4560..7cb09c8c4d8a 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -77,10 +77,28 @@ enum {
IFLA_IPTUN_ENCAP_DPORT,
IFLA_IPTUN_COLLECT_METADATA,
IFLA_IPTUN_FWMARK,
+ IFLA_IPTUN_RULE_VERSION,
+ IFLA_IPTUN_RULE_ACTION,
+ IFLA_IPTUN_RULE_IPV6_PREFIX,
+ IFLA_IPTUN_RULE_IPV6_PREFIXLEN,
+ IFLA_IPTUN_RULE_IPV4_PREFIX,
+ IFLA_IPTUN_RULE_IPV4_PREFIXLEN,
+ IFLA_IPTUN_RULE_EA_LENGTH,
+ IFLA_IPTUN_RULE_PSID_OFFSET,
__IFLA_IPTUN_MAX,
};
#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
+enum map_rule_versions {
+ MAP_VERSION_RFC,
+ MAP_VERSION_DRAFT3,
+};
+
+enum tunnel_rule_actions {
+ TUNNEL_RULE_ACTION_ADD = 1,
+ TUNNEL_RULE_ACTION_DELETE = 2,
+};
+
enum tunnel_encap_types {
TUNNEL_ENCAP_NONE,
TUNNEL_ENCAP_FOU,
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index a9d06d4dd057..e13dc5bd09b5 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -20,6 +20,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/list.h>
+#include <linux/list_sort.h>
#include <linux/module.h>
#include <linux/capability.h>
#include <linux/errno.h>
@@ -32,6 +34,7 @@
#include <linux/net.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <linux/icmpv6.h>
#include <linux/init.h>
@@ -124,6 +127,228 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)
return &dev->stats;
}
+int
+ip6_get_addrport(struct iphdr *iph, __be32 *saddr4, __be32 *daddr4,
+ __be16 *sport4, __be16 *dport4, __u8 *proto, int *icmperr)
+{
+ u8 *ptr;
+ struct iphdr *icmpiph = NULL;
+ struct tcphdr *tcph, *icmptcph;
+ struct udphdr *udph, *icmpudph;
+ struct icmphdr *icmph, *icmpicmph;
+
+ *icmperr = 0;
+ *saddr4 = iph->saddr;
+ *daddr4 = iph->daddr;
+ ptr = (u8 *)iph;
+ ptr += iph->ihl * 4;
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ *proto = IPPROTO_TCP;
+ tcph = (struct tcphdr *)ptr;
+ *sport4 = tcph->source;
+ *dport4 = tcph->dest;
+ break;
+ case IPPROTO_UDP:
+ *proto = IPPROTO_UDP;
+ udph = (struct udphdr *)ptr;
+ *sport4 = udph->source;
+ *dport4 = udph->dest;
+ break;
+ case IPPROTO_ICMP:
+ *proto = IPPROTO_ICMP;
+ icmph = (struct icmphdr *)ptr;
+ switch (icmph->type) {
+ case ICMP_DEST_UNREACH:
+ case ICMP_SOURCE_QUENCH:
+ case ICMP_TIME_EXCEEDED:
+ case ICMP_PARAMETERPROB:
+ *icmperr = 1;
+ ptr = (u8 *)icmph;
+ ptr += sizeof(struct icmphdr);
+ icmpiph = (struct iphdr *)ptr;
+ *saddr4 = icmpiph->saddr;
+ *daddr4 = icmpiph->daddr;
+ if (ntohs(iph->tot_len) < icmpiph->ihl * 4 + 12)
+ return -1;
+ ptr += icmpiph->ihl * 4;
+ switch (icmpiph->protocol) {
+ case IPPROTO_TCP:
+ *proto = IPPROTO_TCP;
+ icmptcph = (struct tcphdr *)ptr;
+ *sport4 = icmptcph->source;
+ *dport4 = icmptcph->dest;
+ break;
+ case IPPROTO_UDP:
+ *proto = IPPROTO_UDP;
+ icmpudph = (struct udphdr *)ptr;
+ *sport4 = icmpudph->source;
+ *dport4 = icmpudph->dest;
+ break;
+ case IPPROTO_ICMP:
+ *proto = IPPROTO_ICMP;
+ icmpicmph = (struct icmphdr *)ptr;
+ *sport4 = icmpicmph->un.echo.id;
+ *dport4 = icmpicmph->un.echo.id;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ *sport4 = icmph->un.echo.id;
+ *dport4 = icmph->un.echo.id;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ip6_mape_gen_addr6(struct ip6_tnl_rule *mr, struct in6_addr *addr6,
+ __be32 addr4, __be16 port4)
+{
+ int i, pbw0, pbi0, pbi1;
+ __u32 addr[4];
+ __u32 psid = 0;
+ __u32 mask = 0;
+ __u32 a = ntohl(addr4);
+ __u16 p = ntohs(port4);
+ int psid_prefix_length = 0;
+ int psid_mask;
+ __u32 id0 = 0;
+ __u32 id1 = 0;
+
+ /* If we have no rule then we can return immediately */
+ if (!mr)
+ return -1;
+
+ psid_prefix_length = mr->ea_length - (32 - mr->ipv4_prefixlen);
+
+ /* In some configurations the PSID can overlap the IPv4 address
+ * segment
+ */
+ if (psid_prefix_length < 0)
+ a &= 0xffffffff << (psid_prefix_length * -1);
+
+ /* Find the PSID from the port by shifting the non-PSID bits out */
+ if (psid_prefix_length > 0) {
+ mask = 0xffffffff >> (32 - psid_prefix_length);
+ psid =
+ (p >> (16 - psid_prefix_length - mr->psid_offset)) & mask;
+ }
+
+ /* We start with the IPv6 subnet provided for this rule */
+ for (i = 0; i < 4; ++i)
+ addr[i] = ntohl(mr->ipv6_subnet.s6_addr32[i]);
+
+ /* We then copy in the IPv4 host portion - pbw0 is the starting
+ * 32 bit chunk, pbi0 is the offset into that chunk
+ */
+ if (mr->ipv4_prefixlen < 32) {
+ pbw0 = mr->ipv6_prefixlen >> 5;
+ pbi0 = mr->ipv6_prefixlen & 0x1f;
+ addr[pbw0] |= (a << mr->ipv4_prefixlen) >> pbi0;
+ pbi1 = pbi0 - mr->ipv4_prefixlen;
+ if (pbi1 > 0)
+ addr[pbw0 + 1] |= a << (32 - pbi1);
+ }
+
+ /* The PSID may need to be inserted into the IPv6 address within the
+ * top 64 bits. Again: pbw0 is the chunk, pbi0 is the offset.
+ */
+ if ((psid_prefix_length - mr->psid_offset) > 0) {
+ psid_mask = mask;
+ pbw0 = (mr->ipv6_prefixlen + (32 - mr->ipv4_prefixlen)) >> 5;
+ pbi0 = (mr->ipv6_prefixlen + (32 - mr->ipv4_prefixlen)) & 0x1f;
+
+ pbi1 = pbi0 - (32 - psid_prefix_length);
+
+ if (pbi1 > 0) {
+ addr[pbw0] |= (psid >> pbi1);
+ addr[pbw0 + 1] |= (psid & psid_mask) << (32 - pbi1);
+ } else {
+ addr[pbw0] |= psid << (32 - pbi0 - psid_prefix_length);
+ }
+ }
+
+ /* The format of the interface id changes depending on version */
+ if (mr->version == MAP_VERSION_RFC) {
+ addr[2] |= (a >> 16);
+ addr[3] |= (a << 16);
+ addr[3] |= psid;
+ } else if (mr->version == MAP_VERSION_DRAFT3) {
+ id0 |= a >> 8;
+ id1 |= (a & 0xFF) << 24;
+ id1 |= psid << 8;
+ addr[2] |= id0;
+ addr[3] |= id1;
+ } else {
+ return -1;
+ }
+
+ /* Copy the formed address back into network order */
+ for (i = 0; i < 4; ++i)
+ addr6->s6_addr32[i] = htonl(addr[i]);
+
+ return 0;
+}
+
+static struct ip6_tnl_rule *ip6_tnl_rule_find(struct net_device *dev,
+ __be32 _dst)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *pos = NULL;
+ u32 dst = ntohl(_dst);
+
+ list_for_each_entry(pos, &t->rules.list, list) {
+ int mask =
+ 0xFFFFFFFF ^ ((1 << (32 - pos->data.ipv4_prefixlen)) - 1);
+ if ((dst & mask) == ntohl(pos->data.ipv4_subnet.s_addr))
+ return &pos->data;
+ }
+ return NULL;
+}
+
+static void
+ip6_tnl_mape_dst(struct net_device *dev, struct sk_buff *skb,
+ struct flowi6 *fl6)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct iphdr *iph;
+ __be32 saddr4, daddr4, addr;
+ __be16 sport4, dport4, port;
+ __u8 proto;
+ int icmperr;
+ struct ip6_tnl_rule *mr = NULL;
+
+ /* If we have no rules then packets always go to the tunnel dst */
+ if (list_empty(&t->rules.list))
+ return;
+
+ /* Find the src / dst address / port (or ID for ICMP) */
+ iph = ip_hdr(skb);
+ ip6_get_addrport(iph, &saddr4, &daddr4, &sport4, &dport4, &proto,
+ &icmperr);
+
+ if (icmperr) {
+ mr = ip6_tnl_rule_find(dev, saddr4);
+ addr = saddr4;
+ port = sport4;
+ } else {
+ mr = ip6_tnl_rule_find(dev, daddr4);
+ addr = daddr4;
+ port = dport4;
+ }
+
+ /* Generate an destination IPv6 address from the internal packet */
+ if (mr)
+ ip6_mape_gen_addr6(mr, &fl6->daddr, addr, port);
+}
+
/**
* ip6_tnl_lookup - fetch tunnel matching the end-point addresses
* @remote: the address of the tunnel exit-point
@@ -207,6 +432,33 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
return &ip6n->tnls[prio][h];
}
+/**
+ * ip6_tnl_bucket_r_any - get head of list matching given tunnel parameters
+ * @p: parameters containing tunnel end-points
+ *
+ * Description:
+ * ip6_tnl_bucket_r_any() returns the head of the list matching the
+ * &struct in6_addr entry laddr in @p.
+ *
+ * Return: head of IPv6 tunnel list
+ **/
+
+static struct ip6_tnl __rcu **
+ip6_tnl_bucket_r_any(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
+{
+ const struct in6_addr *local = &p->laddr;
+ unsigned int h = 0;
+ int prio = 0;
+ struct in6_addr any;
+
+ if (!ipv6_addr_any(local)) {
+ memset(&any, 0, sizeof(any));
+ prio = 1;
+ h = HASH(&any, local);
+ }
+ return &ip6n->tnls[prio][h];
+}
+
/**
* ip6_tnl_link - add tunnel to hash table
* @t: tunnel to be added
@@ -215,7 +467,12 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
static void
ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
{
- struct ip6_tnl __rcu **tp = ip6_tnl_bucket(ip6n, &t->parms);
+ struct ip6_tnl __rcu **tp;
+
+ if (!list_empty(&t->rules.list))
+ tp = ip6_tnl_bucket_r_any(ip6n, &t->parms);
+ else
+ tp = ip6_tnl_bucket(ip6n, &t->parms);
if (t->parms.collect_md)
rcu_assign_pointer(ip6n->collect_md_tun, t);
@@ -234,6 +491,15 @@ ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
struct ip6_tnl __rcu **tp;
struct ip6_tnl *iter;
+ for (tp = ip6_tnl_bucket_r_any(ip6n, &t->parms);
+ (iter = rtnl_dereference(*tp)) != NULL;
+ tp = &iter->next) {
+ if (t == iter) {
+ rcu_assign_pointer(*tp, t->next);
+ return;
+ }
+ }
+
if (t->parms.collect_md)
rcu_assign_pointer(ip6n->collect_md_tun, NULL);
@@ -352,6 +618,16 @@ static struct ip6_tnl *ip6_tnl_locate(struct net *net,
struct ip6_tnl *t;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ for (tp = ip6_tnl_bucket_r_any(ip6n, p);
+ (t = rtnl_dereference(*tp)) != NULL;
+ tp = &t->next) {
+ if (ipv6_addr_equal(local, &t->parms.laddr)) {
+ if (create)
+ return ERR_PTR(-EEXIST);
+ return t;
+ }
+ }
+
for (tp = ip6_tnl_bucket(ip6n, p);
(t = rtnl_dereference(*tp)) != NULL;
tp = &t->next) {
@@ -880,6 +1156,64 @@ static const struct tnl_ptk_info tpi_v4 = {
.proto = htons(ETH_P_IP),
};
+static int ip6_tnl_ipv4_addr_match(struct ip6_tnl *t, __be32 addr)
+{
+ struct in_device *ind;
+ int ret = 0;
+
+ ind = in_dev_get(t->dev);
+ if (!ind)
+ return 0;
+
+ for_ifa(ind) {
+ if (ifa->ifa_address == addr) {
+ ret = 1;
+ break;
+ }
+ }
+ endfor_ifa(ind);
+ in_dev_put(ind);
+
+ return ret;
+}
+
+static int ip6_tnl_map_rule_validate(struct ip6_tnl *t, struct sk_buff *skb)
+{
+ struct ipv6hdr *ipv6h;
+ struct iphdr *iph;
+ __be32 src, dst;
+ __be16 sport, dport;
+ __u8 proto;
+ int icmperr;
+ struct ip6_tnl_rule *mr = NULL;
+ struct in6_addr src6 = { };
+
+ if (likely(list_empty(&t->rules.list)))
+ return 1;
+
+ iph = ipip_hdr(skb);
+ ip6_get_addrport(iph, &src, &dst, &sport, &dport, &proto, &icmperr);
+
+ /* Check if the destination of the packet is for the MAP-E tunnel. */
+ if (!ip6_tnl_ipv4_addr_match(t, dst))
+ return 0;
+
+ /* if the packet's destination is for the tunnel, we need to check
+ * if the source ipv4 address matches any map rules. If there is
+ * no matching rule, just return success (the packet is from br).
+ * Otherwise, we check if the source ipv6 address is valid.
+ */
+ mr = ip6_tnl_rule_find(t->dev, src);
+ if (mr) {
+ ip6_mape_gen_addr6(mr, &src6, src, sport);
+ ipv6h = ipv6_hdr(skb);
+ if (!ipv6_addr_equal(&src6, &ipv6h->saddr))
+ return 0;
+ }
+
+ return 1;
+}
+
static int ipxip6_rcv(struct sk_buff *skb, u8 ipproto,
const struct tnl_ptk_info *tpi,
int (*dscp_ecn_decapsulate)(const struct ip6_tnl *t,
@@ -905,6 +1239,8 @@ static int ipxip6_rcv(struct sk_buff *skb, u8 ipproto,
goto drop;
if (iptunnel_pull_header(skb, 0, tpi->proto, false))
goto drop;
+ if (!ip6_tnl_map_rule_validate(t, skb))
+ goto drop;
if (t->parms.collect_md) {
tun_dst = ipv6_tun_rx_dst(skb, 0, 0, 0);
if (!tun_dst)
@@ -1092,6 +1428,12 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
use_cache = true;
}
+ /* In MAP-E mode we need to always do a route lookup */
+ if (!list_empty(&t->rules.list)) {
+ use_cache = false;
+ ip6_tnl_mape_dst(dev, skb, fl6);
+ }
+
if (use_cache)
dst = dst_cache_get(&t->dst_cache);
@@ -1863,6 +2205,8 @@ ip6_tnl_dev_init_gen(struct net_device *dev)
dev->min_mtu = ETH_MIN_MTU;
dev->max_mtu = IP6_MAX_MTU - dev->hard_header_len;
+ INIT_LIST_HEAD(&t->rules.list);
+
return 0;
destroy_dst:
@@ -1968,6 +2312,95 @@ static void ip6_tnl_netlink_parms(struct nlattr *data[],
if (data[IFLA_IPTUN_FWMARK])
parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]);
+
+ if (data[IFLA_IPTUN_RULE_ACTION])
+ parms->rule_action = nla_get_u8(data[IFLA_IPTUN_RULE_ACTION]);
+
+ if (data[IFLA_IPTUN_RULE_VERSION])
+ parms->rule.version = nla_get_u8(data[IFLA_IPTUN_RULE_VERSION]);
+
+ if (data[IFLA_IPTUN_RULE_IPV6_PREFIX])
+ parms->rule.ipv6_subnet =
+ nla_get_in6_addr(data[IFLA_IPTUN_RULE_IPV6_PREFIX]);
+
+ if (data[IFLA_IPTUN_RULE_IPV6_PREFIXLEN])
+ parms->rule.ipv6_prefixlen =
+ nla_get_u8(data[IFLA_IPTUN_RULE_IPV6_PREFIXLEN]);
+
+ if (data[IFLA_IPTUN_RULE_IPV4_PREFIX])
+ parms->rule.ipv4_subnet.s_addr =
+ nla_get_in_addr(data[IFLA_IPTUN_RULE_IPV4_PREFIX]);
+
+ if (data[IFLA_IPTUN_RULE_IPV4_PREFIXLEN])
+ parms->rule.ipv4_prefixlen =
+ nla_get_u8(data[IFLA_IPTUN_RULE_IPV4_PREFIXLEN]);
+
+ if (data[IFLA_IPTUN_RULE_EA_LENGTH])
+ parms->rule.ea_length =
+ nla_get_u8(data[IFLA_IPTUN_RULE_EA_LENGTH]);
+
+ if (data[IFLA_IPTUN_RULE_PSID_OFFSET])
+ parms->rule.psid_offset =
+ nla_get_u8(data[IFLA_IPTUN_RULE_PSID_OFFSET]);
+}
+
+static int ip6_tnl_rule_cmp(void *unused, struct list_head *_a,
+ struct list_head *_b)
+{
+ struct ip6_rule_list *a = list_entry(_a, struct ip6_rule_list, list);
+ struct ip6_rule_list *b = list_entry(_b, struct ip6_rule_list, list);
+ int diff;
+
+ diff = b->data.ipv4_prefixlen - a->data.ipv4_prefixlen;
+ if (!diff)
+ diff = b->data.ipv4_subnet.s_addr - a->data.ipv4_subnet.s_addr;
+
+ if (!diff)
+ diff = b->data.ipv6_prefixlen - a->data.ipv6_prefixlen;
+
+ if (!diff)
+ diff =
+ memcmp(&b->data.ipv6_subnet, &a->data.ipv6_subnet,
+ sizeof(b->data.ipv6_subnet));
+
+ if (!diff)
+ diff = b->data.ea_length - a->data.ea_length;
+
+ if (!diff)
+ diff = b->data.psid_offset - a->data.psid_offset;
+
+ return diff;
+}
+
+static void ip6_tnl_add_rule(struct net_device *dev,
+ struct __ip6_tnl_parm *new_cfg)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *new_rule = kmalloc(sizeof(t->rules), GFP_KERNEL);
+
+ if (!new_rule)
+ return;
+
+ memcpy(&new_rule->data, &new_cfg->rule, sizeof(new_rule->data));
+ INIT_LIST_HEAD(&new_rule->list);
+ list_add(&new_rule->list, &t->rules.list);
+ list_sort(NULL, &t->rules.list, ip6_tnl_rule_cmp);
+}
+
+static void ip6_tnl_delete_rule(struct net_device *dev,
+ struct __ip6_tnl_parm *_old_rule)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *pos = NULL;
+ struct ip6_rule_list old_rule = { .data = _old_rule->rule };
+ struct ip6_rule_list *iter = NULL;
+
+ list_for_each_entry_safe(pos, iter, &t->rules.list, list) {
+ if (ip6_tnl_rule_cmp(NULL, &pos->list, &old_rule.list) == 0) {
+ list_del(&pos->list);
+ break;
+ }
+ }
}
static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[],
@@ -2035,6 +2468,16 @@ static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev,
err = ip6_tnl_create2(dev);
if (!err && tb[IFLA_MTU])
ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
+ if (!err && data[IFLA_IPTUN_RULE_ACTION]) {
+ switch (nla_get_u8(data[IFLA_IPTUN_RULE_ACTION])) {
+ case TUNNEL_RULE_ACTION_ADD:
+ ip6_tnl_add_rule(dev, &nt->parms);
+ break;
+ case TUNNEL_RULE_ACTION_DELETE:
+ ip6_tnl_delete_rule(dev, &nt->parms);
+ break;
+ }
+ }
return err;
}
@@ -2048,6 +2491,7 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
struct net *net = t->net;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
struct ip_tunnel_encap ipencap;
+ int err;
if (dev == ip6n->fb_tnl_dev)
return -EINVAL;
@@ -2069,13 +2513,32 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
} else
t = netdev_priv(dev);
- return ip6_tnl_update(t, &p);
+ err = ip6_tnl_update(t, &p);
+ if (!err && data[IFLA_IPTUN_RULE_ACTION]) {
+ switch (nla_get_u8(data[IFLA_IPTUN_RULE_ACTION])) {
+ case TUNNEL_RULE_ACTION_ADD:
+ ip6_tnl_add_rule(dev, &p);
+ break;
+ case TUNNEL_RULE_ACTION_DELETE:
+ ip6_tnl_delete_rule(dev, &p);
+ break;
+ }
+ }
+
+ return err;
}
static void ip6_tnl_dellink(struct net_device *dev, struct list_head *head)
{
struct net *net = dev_net(dev);
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *pos = NULL;
+ struct ip6_rule_list *iter = NULL;
+
+ list_for_each_entry_safe(pos, iter, &t->rules.list, list) {
+ list_del(&pos->list);
+ }
if (dev != ip6n->fb_tnl_dev)
unregister_netdevice_queue(dev, head);
@@ -2112,6 +2575,22 @@ static size_t ip6_tnl_get_size(const struct net_device *dev)
nla_total_size(0) +
/* IFLA_IPTUN_FWMARK */
nla_total_size(4) +
+ /* IFLA_IPTUN_RULE_VERSION */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_ACTION */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_IPV6_PREFIXLEN */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_IPV4_PREFIXLEN */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_EA_LENGTH */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_PSID_OFFSET */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_IPV6_PREFIX */
+ nla_total_size(sizeof(struct in6_addr)) +
+ /* IFLA_IPTUN_RULE_IPV4_PREFIX */
+ nla_total_size(sizeof(struct in6_addr)) +
0;
}
@@ -2170,6 +2649,14 @@ static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = {
[IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 },
[IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG },
[IFLA_IPTUN_FWMARK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_RULE_VERSION] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_ACTION] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_IPV6_PREFIXLEN] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_IPV4_PREFIXLEN] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_EA_LENGTH] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_PSID_OFFSET] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_IPV6_PREFIX] = { .len = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_RULE_IPV4_PREFIX] = { .len = sizeof(struct in_addr) },
};
static struct rtnl_link_ops ip6_link_ops __read_mostly = {
--
2.19.1
^ permalink raw reply related
* Re: [Patch net] net: invert the check of detecting hardware RX checksum fault
From: Eric Dumazet @ 2018-11-20 1:47 UTC (permalink / raw)
To: Cong Wang, Eric Dumazet
Cc: Herbert Xu, Linux Kernel Network Developers, Tom Herbert,
Eric Dumazet, Saeed Mahameed
In-Reply-To: <CAM_iQpWyEr1xQ_c_yZf8NfULBsm=kv_-xN4PB0tT=RxCAz1c5A@mail.gmail.com>
On 11/19/2018 05:42 PM, Cong Wang wrote:
> On Fri, Nov 16, 2018 at 12:15 PM Cong Wang <xiyou.wangcong@gmail.com> wrote:
>>
>> On Thu, Nov 15, 2018 at 8:52 PM Eric Dumazet <eric.dumazet@gmail.com> wrote:
>>>
>>> You could use trafgen to cook such a frame and confirm the theory.
>>>
>>> Something like :
>>
>> I will try it.
>
> I just tried it, it doesn't make much difference, the warning only
> shows up once after I ran the trafgen script for many times,
> it could be triggered by other daemons running on the host too.
I guess we will need to dump the whole packet for debugging,
as suggest by Herbert :/
^ permalink raw reply
* Re: [PATCH v1 1/2] ip6_tunnel: Adding support of mapping rules for MAP-E tunnel
From: David Miller @ 2018-11-20 1:46 UTC (permalink / raw)
To: felix.jia; +Cc: pablo, blair.steven, netdev, sheena.mira-ato, masakazu.asama
In-Reply-To: <20181120013114.22020-1-felix.jia@alliedtelesis.co.nz>
Please do not submit completely unrelated changes together in a patch
series.
Thank you.
^ permalink raw reply
* [PATCH net-next] tcp: drop dst in tcp_add_backlog()
From: Eric Dumazet @ 2018-11-20 1:45 UTC (permalink / raw)
To: David S . Miller; +Cc: netdev, Eric Dumazet, Eric Dumazet
Under stress, softirq rx handler often hits a socket owned by the user,
and has to queue the packet into socket backlog.
When this happens, skb dst refcount is taken before we escape rcu
protected region. This is done from __sk_add_backlog() calling
skb_dst_force().
Consumer will have to perform the opposite costly operation.
AFAIK nothing in tcp stack requests the dst after skb was stored
in the backlog. If this was the case, we would have had failures
already since skb_dst_force() can end up clearing skb dst anyway.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_ipv4.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 0952d4b772e7aa3cfb936596aa586dbd2dd6efde..795605a2327504b8a025405826e7e0ca8dc8501d 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1634,6 +1634,8 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
*/
skb_condense(skb);
+ skb_dst_drop(skb);
+
if (unlikely(sk_add_backlog(sk, skb, limit))) {
bh_unlock_sock(sk);
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPBACKLOGDROP);
--
2.19.1.1215.g8438c0b245-goog
^ permalink raw reply related
* Re: [PATCH 10/11] nvmet-tcp: add NVMe over TCP target driver
From: David Miller @ 2018-11-20 1:44 UTC (permalink / raw)
To: sagi; +Cc: sagi, linux-block, netdev, keith.busch, hch, linux-nvme
In-Reply-To: <0de07d6d-ed90-df0e-5710-1f5cdddd6187@grimberg.me>
From: Sagi Grimberg <sagi@grimberg.me>
Date: Mon, 19 Nov 2018 15:24:13 -0800
>
>>> Also, looking a bit closer there is a slight difference between the
>>> copy vs. the copy_and_csum variants. copy allows for a short_copy if
>>> we copy less than we expect while the csum faults it. I'm thinking
>>> that the copy_and_hash variant should also fault? Although I'm not
>>> sure I understand the fault entirely as csum is supposed to be
>>> cumulative, any insight?
>> When we are writing and signal an error, sockets have this recurring
>> pattern where we return immediately the amount of bytes successfully
>> transferred. Then on the next sendmsg() call we give the error.
>> I don't know if that is what is influencing the behavior here or not
>> but it could be.
>
> That makes sense... Does recvmsg() have the same semantics? this is
> the
> rx path where we copy fragments to an iter..
I just checked and TCP at the very least behaves this way on recvmsg()
(report short length, then -EFAULT on next recvmsg() call).
It maintains a local variable "copied" which is increased every time
skb_copy_datagram_msg() is successful. If in a subsequent iteration
of the loop skb_copy_datagram_msg() returns -EFAULT, it will instead
return 'copied' from recvmsg() if non-zero else the -EFAULT.
^ permalink raw reply
* Re: [Patch net] net: invert the check of detecting hardware RX checksum fault
From: Cong Wang @ 2018-11-20 1:42 UTC (permalink / raw)
To: Eric Dumazet
Cc: Herbert Xu, Linux Kernel Network Developers, Tom Herbert,
Eric Dumazet, Saeed Mahameed
In-Reply-To: <CAM_iQpX8jpfq1CtavmsOvcfTW=ecPeUn5gJFNG1Z4NU+_urv6Q@mail.gmail.com>
On Fri, Nov 16, 2018 at 12:15 PM Cong Wang <xiyou.wangcong@gmail.com> wrote:
>
> On Thu, Nov 15, 2018 at 8:52 PM Eric Dumazet <eric.dumazet@gmail.com> wrote:
> >
> > You could use trafgen to cook such a frame and confirm the theory.
> >
> > Something like :
>
> I will try it.
I just tried it, it doesn't make much difference, the warning only
shows up once after I ran the trafgen script for many times,
it could be triggered by other daemons running on the host too.
^ permalink raw reply
* Re: [PATCH iproute2-next 2/8] json: add %hhu helpers
From: Jakub Kicinski @ 2018-11-20 1:40 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dsahern, oss-drivers, netdev
In-Reply-To: <20181119171842.1d1e94ad@xeon-e3>
On Mon, 19 Nov 2018 17:18:42 -0800, Stephen Hemminger wrote:
> > void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
> > {
> > jsonw_name(self, prop);
>
> Do you really need this? it turns out that because of C type
> conversions print_uint should just work?
I wondered about that for a second, but I took the existence of
jsonw_hu_field() etc. as a proof that explicit typing is preferred.
^ permalink raw reply
* Re: [PATCH iproute2] bpf: initialise map symbol before retrieving and comparing its type
From: Y Song @ 2018-11-20 1:38 UTC (permalink / raw)
To: Quentin Monnet
Cc: David Ahern, stephen, Yonghong Song, Alexei Starovoitov,
Daniel Borkmann, netdev, oss-drivers
In-Reply-To: <1542677187-25432-1-git-send-email-quentin.monnet@netronome.com>
On Mon, Nov 19, 2018 at 5:28 PM Quentin Monnet
<quentin.monnet@netronome.com> wrote:
>
> In order to compare BPF map symbol type correctly in regard to the
> latest LLVM, commit 7a04dd84a7f9 ("bpf: check map symbol type properly
> with newer llvm compiler") compares map symbol type to both NOTYPE and
> OBJECT. To do so, it first retrieves the type from "sym.st_info" and
> stores it into a temporary variable.
>
> However, the type is collected from the symbol "sym" before this latter
> symbol is actually updated. gelf_getsym() is called after that and
> updates "sym", and when comparison with OBJECT or NOTYPE happens it is
> done on the type of the symbol collected in the previous passage of the
> loop (or on an uninitialised symbol on the first passage). This may
> eventually break map collection from the ELF file.
>
> Fix this by assigning the type to the temporary variable only after the
> call to gelf_getsym().
>
> Fixes: 7a04dd84a7f9 ("bpf: check map symbol type properly with newer llvm compiler")
> Reported-by: Ron Philip <ron.philip@netronome.com>
> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> Reviewed-by: Jiong Wang <jiong.wang@netronome.com>
Thanks for the fix!
Acked-by: Yonghong Song <yhs@fb.com>
> ---
> lib/bpf.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/lib/bpf.c b/lib/bpf.c
> index 45f279fa4a41..6aff8f7bad7f 100644
> --- a/lib/bpf.c
> +++ b/lib/bpf.c
> @@ -1758,11 +1758,12 @@ static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which)
> int i;
>
> for (i = 0; i < ctx->sym_num; i++) {
> - int type = GELF_ST_TYPE(sym.st_info);
> + int type;
>
> if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
> continue;
>
> + type = GELF_ST_TYPE(sym.st_info);
> if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
> (type != STT_NOTYPE && type != STT_OBJECT) ||
> sym.st_shndx != ctx->sec_maps ||
> @@ -1851,11 +1852,12 @@ static int bpf_map_num_sym(struct bpf_elf_ctx *ctx)
> GElf_Sym sym;
>
> for (i = 0; i < ctx->sym_num; i++) {
> - int type = GELF_ST_TYPE(sym.st_info);
> + int type;
>
> if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
> continue;
>
> + type = GELF_ST_TYPE(sym.st_info);
> if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
> (type != STT_NOTYPE && type != STT_OBJECT) ||
> sym.st_shndx != ctx->sec_maps)
> @@ -1931,10 +1933,12 @@ static int bpf_map_verify_all_offs(struct bpf_elf_ctx *ctx, int end)
> * the table again.
> */
> for (i = 0; i < ctx->sym_num; i++) {
> - int type = GELF_ST_TYPE(sym.st_info);
> + int type;
>
> if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
> continue;
> +
> + type = GELF_ST_TYPE(sym.st_info);
> if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
> (type != STT_NOTYPE && type != STT_OBJECT) ||
> sym.st_shndx != ctx->sec_maps)
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH bpf-next 1/2] bpf: adding support for map in map in libbpf
From: Nikita V. Shirokov @ 2018-11-20 1:38 UTC (permalink / raw)
To: Y Song; +Cc: Alexei Starovoitov, Daniel Borkmann, Jakub Kicinski, netdev
In-Reply-To: <CAH3MdRVAWsk9RY4qO3sCyFhVM8FYR+em4cNuYcXpuZCtEYv4pA@mail.gmail.com>
On Mon, Nov 19, 2018 at 05:12:43PM -0800, Y Song wrote:
> On Mon, Nov 19, 2018 at 4:13 PM Nikita V. Shirokov <tehnerd@tehnerd.com> wrote:
> >
> > idea is pretty simple. for specified map (pointed by struct bpf_map)
> > we would provide descriptor of already loaded map, which is going to be
> > used as a prototype for inner map. proposed workflow:
> > 1) open bpf's object (bpf_object__open)
> > 2) create bpf's map which is going to be used as a prototype
> > 3) find (by name) map-in-map which you want to load and update w/
> > descriptor of inner map w/ a new helper from this patch
> > 4) load bpf program w/ bpf_object__load
> >
> > inner_map_fd is ignored by any other maps asidef from (hash|array) of
> > maps
> >
> > Signed-off-by: Nikita V. Shirokov <tehnerd@tehnerd.com>
> > ---
> > tools/lib/bpf/libbpf.c | 7 +++++++
> > tools/lib/bpf/libbpf.h | 2 ++
> > 2 files changed, 9 insertions(+)
> >
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index a01eb9584e52..a2ee1b1a93b6 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
> > @@ -163,6 +163,7 @@ struct bpf_map {
> > char *name;
> > size_t offset;
> > int map_ifindex;
> > + int inner_map_fd;
> > struct bpf_map_def def;
> > __u32 btf_key_type_id;
> > __u32 btf_value_type_id;
> > @@ -1146,6 +1147,7 @@ bpf_object__create_maps(struct bpf_object *obj)
> > create_attr.btf_fd = 0;
> > create_attr.btf_key_type_id = 0;
> > create_attr.btf_value_type_id = 0;
> > + create_attr.inner_map_fd = map->inner_map_fd;
> >
> > if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
> > create_attr.btf_fd = btf__fd(obj->btf);
> > @@ -2562,6 +2564,11 @@ void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
> > map->map_ifindex = ifindex;
> > }
> >
> > +void bpf_map__add_inner_map_fd(struct bpf_map *map, const int fd)
>
> Do we need "const" attribute here?
>
i can drop it in v2
> > +{
> > + map->inner_map_fd = fd;
> > +}
> > +
> > static struct bpf_map *
> > __bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
> > {
> > diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> > index b1686a787102..7cb00cd41789 100644
> > --- a/tools/lib/bpf/libbpf.h
> > +++ b/tools/lib/bpf/libbpf.h
> > @@ -293,6 +293,8 @@ LIBBPF_API void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
> > LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
> > LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
> >
> > +LIBBPF_API void bpf_map__add_inner_map_fd(struct bpf_map *map, const int fd);
> > +
> > LIBBPF_API long libbpf_get_error(const void *ptr);
> >
> > struct bpf_prog_load_attr {
> > --
> > 2.15.1
> >
^ permalink raw reply
* Re: [PATCH bpf-next 2/2] bpf: adding tests for mapinmap helpber in libbpf
From: Nikita V. Shirokov @ 2018-11-20 1:37 UTC (permalink / raw)
To: Y Song; +Cc: Alexei Starovoitov, Daniel Borkmann, Jakub Kicinski, netdev
In-Reply-To: <CAH3MdRXQ7WRTnN_SKG04yHxNC8xvFWef_MDi3Fw3EsW+YLs7ZQ@mail.gmail.com>
O Mon, Nov 19, 2018 at 05:18:46PM -0800, Y Song wrote:
> On Mon, Nov 19, 2018 at 4:13 PM Nikita V. Shirokov <tehnerd@tehnerd.com> wrote:
> >
> > adding test/example of bpf_map__add_inner_map_fd usage
> >
> > Signed-off-by: Nikita V. Shirokov <tehnerd@tehnerd.com>
> > ---
> > tools/testing/selftests/bpf/Makefile | 3 +-
> > tools/testing/selftests/bpf/test_mapinmap.c | 53 ++++++++++++++++++++
> > tools/testing/selftests/bpf/test_maps.c | 76 +++++++++++++++++++++++++++++
> > 3 files changed, 131 insertions(+), 1 deletion(-)
> > create mode 100644 tools/testing/selftests/bpf/test_mapinmap.c
> >
> > diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
> > index 57b4712a6276..a3ea69dc9bdf 100644
> > --- a/tools/testing/selftests/bpf/Makefile
> > +++ b/tools/testing/selftests/bpf/Makefile
> > @@ -38,7 +38,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
> > test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
> > get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \
> > test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o \
> > - test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o
> > + test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o \
> > + test_mapinmap.o
> >
> > # Order correspond to 'make run_tests' order
> > TEST_PROGS := test_kmod.sh \
> > diff --git a/tools/testing/selftests/bpf/test_mapinmap.c b/tools/testing/selftests/bpf/test_mapinmap.c
> > new file mode 100644
> > index 000000000000..8aef6c652c9c
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/test_mapinmap.c
> > @@ -0,0 +1,53 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright (c) 2018 Facebook */
> > +#include <stddef.h>
> > +#include <linux/bpf.h>
> > +#include <linux/types.h>
> > +#include "bpf_helpers.h"
> > +
> > +
> nit: extra new line
>
> > +struct bpf_map_def SEC("maps") mim_array = {
> > + .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
> > + .key_size = sizeof(int),
> > + /* must be sizeof(__u32) for map in map */
> > + .value_size = sizeof(__u32),
> > + .max_entries = 1,
> > + .map_flags = 0,
> > +};
> > +
> > +struct bpf_map_def SEC("maps") mim_hash = {
> > + .type = BPF_MAP_TYPE_HASH_OF_MAPS,
> > + .key_size = sizeof(int),
> > + /* must be sizeof(__u32) for map in map */
> > + .value_size = sizeof(__u32),
> > + .max_entries = 1,
> > + .map_flags = 0,
> > +};
> > +
> > +
> > +
> nit: extra new lines.
> > +SEC("xdp_mimtest")
> > +int xdp_mimtest0(struct xdp_md *ctx)
> > +{
> > + int value = 123;
> > + int key = 0;
> > + void *map;
> > +
> > + map = bpf_map_lookup_elem(&mim_array, &key);
> > + if (!map)
> > + return XDP_DROP;
> > +
> > + bpf_map_update_elem(map, &key, &value, 0);
> > +
> > + map = bpf_map_lookup_elem(&mim_hash, &key);
> > + if (!map)
> > + return XDP_DROP;
> > +
> > + bpf_map_update_elem(map, &key, &value, 0);
> > +
> > + return XDP_PASS;
> > +}
> > +
> > +
> nit: extra new line
> > +int _version SEC("version") = 1;
> > +char _license[] SEC("license") = "GPL";
> > diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
> > index 4db2116e52be..a49ab294971d 100644
> > --- a/tools/testing/selftests/bpf/test_maps.c
> > +++ b/tools/testing/selftests/bpf/test_maps.c
> > @@ -1080,6 +1080,80 @@ static void test_sockmap(int tasks, void *data)
> > exit(1);
> > }
> >
> > +#define MAPINMAP_PROG "./test_mapinmap.o"
> > +static void test_mapinmap(void)
> > +{
> > + struct bpf_program *prog;
> > + struct bpf_object *obj;
> > + struct bpf_map *map;
> > + int mim_fd, fd;
> > + int pos = 0;
> > +
> > + obj = bpf_object__open(MAPINMAP_PROG);
> > +
> > + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(int),
> > + 2, 0);
> > + if (fd < 0) {
> > + printf("Failed to create hashmap '%s'!\n", strerror(errno));
> > + exit(1);
> > + }
> > + printf("fd is %d\n", fd);
> not sure whether you need to print fd in the console or not. Any particular
> reason?
was using for debugging. forget to remove.
> > +
> > + map = bpf_object__find_map_by_name(obj, "mim_array");
> > + if (IS_ERR(map)) {
> > + printf("Failed to load array of maps from test prog\n");
> > + goto out_mapinmap;
> > + }
> > + bpf_map__add_inner_map_fd(map, fd);
> > +
> > + map = bpf_object__find_map_by_name(obj, "mim_hash");
> > + if (IS_ERR(map)) {
> > + printf("Failed to load hash of maps from test prog\n");
> > + goto out_mapinmap;
> > + }
> > + bpf_map__add_inner_map_fd(map, fd);
> > +
> > +
> nit: extra new line
> > + bpf_object__for_each_program(prog, obj) {
> > + bpf_program__set_xdp(prog);
> > + }
> > + bpf_object__load(obj);
> > +
> > + map = bpf_object__find_map_by_name(obj, "mim_array");
> > + if (IS_ERR(map)) {
> > + printf("Failed to load array of maps from test prog\n");
> > + goto out_mapinmap;
> > + }
> > + mim_fd = bpf_map__fd(map);
> > + if (mim_fd < 0) {
> > + printf("Failed to get descriptor for array of maps\n");
> > + goto out_mapinmap;
> > + }
> > +
> > + bpf_map_update_elem(mim_fd, &pos, &fd, 0);
> do you want to test return value of bpf_map_update_elem() to make sure
> update is successful?
sure.
> > +
> > + map = bpf_object__find_map_by_name(obj, "mim_hash");
> > + if (IS_ERR(map)) {
> > + printf("Failed to load hash of maps from test prog\n");
> > + goto out_mapinmap;
> > + }
> > + mim_fd = bpf_map__fd(map);
> > + if (mim_fd < 0) {
> > + printf("Failed to get descriptor for hash of maps\n");
> > + goto out_mapinmap;
> > + }
> > + bpf_map_update_elem(mim_fd, &pos, &fd, 0);
> do you want to test return value of bpf_map_update_elem()?
> > +
> > +
> nit: extra new line
> > + close(fd);
> > + bpf_object__close(obj);
> > + return;
> > +
> > +out_mapinmap:
> > + close(fd);
> > + exit(1);
> > +}
> > +
> > #define MAP_SIZE (32 * 1024)
> >
> > static void test_map_large(void)
> > @@ -1554,6 +1628,8 @@ static void run_all_tests(void)
> >
> > test_queuemap(0, NULL);
> > test_stackmap(0, NULL);
> > +
> > + test_mapinmap();
> > }
> >
> > int main(void)
> > --
> > 2.15.1
> >
^ permalink raw reply
* Re: [PATCH net-next 00/11] r8169: series with further smaller improvements
From: David Miller @ 2018-11-20 1:32 UTC (permalink / raw)
To: hkallweit1; +Cc: nic_swsd, netdev
In-Reply-To: <7484b166-100b-52b9-1539-22e9199a8a3a@gmail.com>
From: Heiner Kallweit <hkallweit1@gmail.com>
Date: Mon, 19 Nov 2018 22:30:25 +0100
> Again nothing exciting, just smaller improvements.
Series applied, thank you!
^ permalink raw reply
* [PATCH v1 2/2] netfilter: Add PSID mode to MASQUERADE
From: Felix Jia @ 2018-11-20 1:31 UTC (permalink / raw)
To: David S . Miller, Pablo Neira Ayuso
Cc: Blair Steven, netdev, Anthony Lineham, Scott Parlane, Felix Jia
In-Reply-To: <20181120013114.22020-1-felix.jia@alliedtelesis.co.nz>
From: Blair Steven <blair.steven@alliedtelesis.co.nz>
This adds support for masquerading into a smaller subset of ports -
defined by the PSID values from RFC-7597 Section 5.1. This is part of
the support for MAP-E, which allows multiple devices to share an IPv4
address by splitting the L4 port / id into ranges by both masquerading
and encapsulating IPv4 packets inside an IPv6 carrier.
Co-developed-by: Anthony Lineham <anthony.lineham@alliedtelesis.co.nz>
Co-developed-by: Scott Parlane <scott.parlane@alliedtelesis.co.nz>
Signed-off-by: Blair Steven <blair.steven@alliedtelesis.co.nz>
Signed-off-by: Anthony Lineham <anthony.lineham@alliedtelesis.co.nz>
Signed-off-by: Scott Parlane <scott.parlane@alliedtelesis.co.nz>
Signed-off-by: Felix Jia <felix.jia@alliedtelesis.co.nz>
---
include/net/netfilter/nf_nat_l4proto.h | 6 +--
.../netfilter/nf_conntrack_tuple_common.h | 5 ++
include/uapi/linux/netfilter/nf_nat.h | 4 +-
net/ipv4/netfilter/nf_nat_proto_icmp.c | 51 ++++++++++++++++++-
net/netfilter/nf_nat_core.c | 25 ++++++---
net/netfilter/nf_nat_proto_common.c | 51 +++++++++++++++++--
net/netfilter/nf_nat_proto_unknown.c | 3 +-
7 files changed, 126 insertions(+), 19 deletions(-)
diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h
index b4d6b29bca62..d3fb8f138d0a 100644
--- a/include/net/netfilter/nf_nat_l4proto.h
+++ b/include/net/netfilter/nf_nat_l4proto.h
@@ -24,8 +24,7 @@ struct nf_nat_l4proto {
/* Is the manipable part of the tuple between min and max incl? */
bool (*in_range)(const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype,
- const union nf_conntrack_man_proto *min,
- const union nf_conntrack_man_proto *max);
+ const struct nf_nat_range2 *range);
/* Alter the per-proto part of the tuple (depending on
* maniptype), to give a unique tuple in the given range if
@@ -67,8 +66,7 @@ extern const struct nf_nat_l4proto nf_nat_l4proto_udplite;
bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype,
- const union nf_conntrack_man_proto *min,
- const union nf_conntrack_man_proto *max);
+ const struct nf_nat_range2 *range);
void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
struct nf_conntrack_tuple *tuple,
diff --git a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
index 64390fac6f7e..36d16d47c2b0 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
@@ -39,6 +39,11 @@ union nf_conntrack_man_proto {
struct {
__be16 key; /* GRE key is 32bit, PPtP only uses 16bit */
} gre;
+ struct {
+ unsigned char psid_length;
+ unsigned char offset;
+ __be16 psid;
+ } psid;
};
#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)
diff --git a/include/uapi/linux/netfilter/nf_nat.h b/include/uapi/linux/netfilter/nf_nat.h
index 4a95c0db14d4..d145d3eca25f 100644
--- a/include/uapi/linux/netfilter/nf_nat.h
+++ b/include/uapi/linux/netfilter/nf_nat.h
@@ -11,6 +11,7 @@
#define NF_NAT_RANGE_PERSISTENT (1 << 3)
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5)
+#define NF_NAT_RANGE_PSID (1 << 6)
#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
(NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
@@ -18,7 +19,8 @@
#define NF_NAT_RANGE_MASK \
(NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \
NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \
- NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET)
+ NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \
+ NF_NAT_RANGE_PSID)
struct nf_nat_ipv4_range {
unsigned int flags;
diff --git a/net/ipv4/netfilter/nf_nat_proto_icmp.c b/net/ipv4/netfilter/nf_nat_proto_icmp.c
index 6d7cf1d79baf..39efac4930b6 100644
--- a/net/ipv4/netfilter/nf_nat_proto_icmp.c
+++ b/net/ipv4/netfilter/nf_nat_proto_icmp.c
@@ -20,9 +20,23 @@
static bool
icmp_in_range(const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype,
- const union nf_conntrack_man_proto *min,
- const union nf_conntrack_man_proto *max)
+ const struct nf_nat_range2 *range)
{
+ const union nf_conntrack_man_proto *min = &range->min_proto;
+ const union nf_conntrack_man_proto *max = &range->max_proto;
+
+ /* In this case we are in PSID mode and the rules are all different */
+ if (range->flags & NF_NAT_RANGE_PSID) {
+ u16 offset_mask = ((1 << min->psid.offset) - 1) <<
+ (16 - min->psid.offset);
+ u16 psid_mask = ((1 << min->psid.psid_length) - 1) <<
+ (16 - min->psid.offset - min->psid.psid_length);
+ u16 id = ntohs(tuple->src.u.icmp.id);
+
+ return ((id & offset_mask) != 0) &&
+ ((id & psid_mask) == min->psid.psid);
+ }
+
return ntohs(tuple->src.u.icmp.id) >= ntohs(min->icmp.id) &&
ntohs(tuple->src.u.icmp.id) <= ntohs(max->icmp.id);
}
@@ -38,6 +52,39 @@ icmp_unique_tuple(const struct nf_nat_l3proto *l3proto,
unsigned int range_size;
unsigned int i;
+ if (range->flags & NF_NAT_RANGE_PSID) {
+ /* m = number of bits in each valid range */
+ u16 off;
+ int m = 16 - range->min_proto.psid.psid_length -
+ range->min_proto.psid.offset;
+
+ range_size = (1 << (16 - range->min_proto.psid.psid_length)) -
+ (1 << m);
+ off = ntohs(tuple->src.u.icmp.id);
+ for (i = 0; ; ++off) {
+ /* Find the non-PSID parts of the Index.
+ * To do this we look for an unused ID that is
+ * comprised of [t_chunk|PSID|b_chunk]. The size of
+ * these pieces is defined by the psid_length and
+ * offset.
+ */
+ int b_chunk = (off % range_size) & ((1 << (m)) - 1);
+ int t_chunk = (((off % range_size) >> m) &
+ ((1 << range->min_proto.psid.offset) -
+ 1)) <<
+ (m + range->min_proto.psid.psid_length);
+ /* Skip the all-zeroes reserved chunk */
+ t_chunk += (1 << (16 - range->min_proto.psid.offset));
+
+ tuple->src.u.icmp.id = htons(t_chunk |
+ (range->min_proto.psid.psid << m)
+ | b_chunk);
+
+ if (++i == range_size || !nf_nat_used_tuple(tuple, ct))
+ return;
+ }
+ }
+
range_size = ntohs(range->max_proto.icmp.id) -
ntohs(range->min_proto.icmp.id) + 1;
/* If no range specified... */
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index e2b196054dfc..18e39af3838d 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -187,9 +187,15 @@ static int in_range(const struct nf_nat_l3proto *l3proto,
!l3proto->in_range(tuple, range))
return 0;
+ /* If we are using PSID mode all protocols need to be checked
+ * to see that they fit inside the range.
+ */
+ if ((range->flags & NF_NAT_RANGE_PSID) &&
+ !l4proto->in_range(tuple, NF_NAT_MANIP_SRC, range))
+ return 0;
+
if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) ||
- l4proto->in_range(tuple, NF_NAT_MANIP_SRC,
- &range->min_proto, &range->max_proto))
+ l4proto->in_range(tuple, NF_NAT_MANIP_SRC, range))
return 1;
return 0;
@@ -369,11 +375,18 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
/* Only bother mapping if it's not already in range and unique */
if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
- if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+ /* Now that the PSID mode is present we always need to check
+ * to see if the source ports are in range.
+ */
+ if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED ||
+ (range->flags & NF_NAT_RANGE_PSID &&
+ !in_range(l3proto, l4proto, orig_tuple, range))) {
+ /* The in_range prototype has been changed to take a
+ * whole range rather than min and max protocol
+ * information.
+ */
if (!(range->flags & NF_NAT_RANGE_PROTO_OFFSET) &&
- l4proto->in_range(tuple, maniptype,
- &range->min_proto,
- &range->max_proto) &&
+ l4proto->in_range(tuple, maniptype, range) &&
(range->min_proto.all == range->max_proto.all ||
!nf_nat_used_tuple(tuple, ct)))
goto out;
diff --git a/net/netfilter/nf_nat_proto_common.c b/net/netfilter/nf_nat_proto_common.c
index 5d849d835561..4ca3b2715e7c 100644
--- a/net/netfilter/nf_nat_proto_common.c
+++ b/net/netfilter/nf_nat_proto_common.c
@@ -19,16 +19,30 @@
bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type maniptype,
- const union nf_conntrack_man_proto *min,
- const union nf_conntrack_man_proto *max)
+ const struct nf_nat_range2 *range)
{
__be16 port;
+ const union nf_conntrack_man_proto *min = &range->min_proto;
+ const union nf_conntrack_man_proto *max = &range->max_proto;
if (maniptype == NF_NAT_MANIP_SRC)
port = tuple->src.u.all;
else
port = tuple->dst.u.all;
+ /* In this case we are in PSID mode and the rules are all different */
+ if (range->flags & NF_NAT_RANGE_PSID) {
+ /* m = number of bits in each valid range */
+ int m = 16 - min->psid.psid_length - min->psid.offset;
+ u16 offset_mask = ((1 << min->psid.offset) - 1) <<
+ (16 - min->psid.offset);
+ u16 psid_mask = ((1 << min->psid.psid_length) - 1) << m;
+
+ return ((ntohs(port) & offset_mask) != 0) &&
+ (((ntohs(port) & psid_mask) >> m) ==
+ min->psid.psid);
+ }
+
return ntohs(port) >= ntohs(min->all) &&
ntohs(port) <= ntohs(max->all);
}
@@ -46,9 +60,38 @@ void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
u_int16_t off;
if (maniptype == NF_NAT_MANIP_SRC)
- portptr = &tuple->src.u.all;
+ portptr = &tuple->src.u.tcp.port;
else
- portptr = &tuple->dst.u.all;
+ portptr = &tuple->dst.u.tcp.port;
+
+ if (range->flags & NF_NAT_RANGE_PSID) {
+ /* Find the non-PSID parts of the port.
+ * To do this we look for an unused port that is
+ * comprised of [t_chunk|PSID|b_chunk]. The size of
+ * these pieces is defined by the psid_length and
+ * offset.
+ */
+ int m = 16 - range->min_proto.psid.psid_length -
+ range->min_proto.psid.offset;
+ range_size = (1 << (16 - range->min_proto.psid.psid_length)) -
+ (1 << m);
+ off = ntohs(*portptr);
+ for (i = 0; ; ++off) {
+ int b_chunk = (off % range_size) & ((1 << (m)) - 1);
+ int t_chunk = (((off % range_size) >> m) &
+ ((1 << range->min_proto.psid.offset) -
+ 1)) <<
+ (m + range->min_proto.psid.psid_length);
+ /* Skip the all-zeroes reserved chunk */
+ t_chunk += (1 << (16 - range->min_proto.psid.offset));
+
+ *portptr = htons(t_chunk |
+ (range->min_proto.psid.psid << m)
+ | b_chunk);
+ if (++i == range_size || !nf_nat_used_tuple(tuple, ct))
+ return;
+ }
+ }
/* If no range specified... */
if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) {
diff --git a/net/netfilter/nf_nat_proto_unknown.c b/net/netfilter/nf_nat_proto_unknown.c
index c5db3e251232..82140ffc706d 100644
--- a/net/netfilter/nf_nat_proto_unknown.c
+++ b/net/netfilter/nf_nat_proto_unknown.c
@@ -19,8 +19,7 @@
static bool unknown_in_range(const struct nf_conntrack_tuple *tuple,
enum nf_nat_manip_type manip_type,
- const union nf_conntrack_man_proto *min,
- const union nf_conntrack_man_proto *max)
+ const struct nf_nat_range2 *range)
{
return true;
}
--
2.19.1
^ permalink raw reply related
* [PATCH v1 1/2] ip6_tunnel: Adding support of mapping rules for MAP-E tunnel
From: Felix Jia @ 2018-11-20 1:31 UTC (permalink / raw)
To: David S . Miller, Pablo Neira Ayuso
Cc: Blair Steven, netdev, Felix Jia, Sheena Mira-ato, Masakazu Asama
From: Blair Steven <blair.steven@alliedtelesis.co.nz>
Mapping of Addresses and Ports with Encapsulation (MAP-E) is defined in
RFC7597, and is an IPv6 transition technology providing interoperability
between IPv4 and IPv6 networks.
MAP-E uses the encapsulation mode described in RFC2473 (IPv6 Tunneling)
to transport IPv4 and IPv6 packets over an IPv6 network. It requires a
list rules for mapping between IPv4 prefix/shared addresses and IPv6
addresses.
This patch also support the mapping rules define in the draft3 version of
the RFC.
Co-developed-by: Felix Jia <felix.jia@alliedtelesis.co.nz>
Co-developed-by: Sheena Mira-ato <sheena.mira-ato@alliedtelesis.co.nz>
Co-developed-by: Masakazu Asama <masakazu.asama@gmail.com>
Signed-off-by: Blair Steven <blair.steven@alliedtelesis.co.nz>
Signed-off-by: Felix Jia <felix.jia@alliedtelesis.co.nz>
Signed-off-by: Sheena Mira-ato <sheena.mira-ato@alliedtelesis.co.nz>
Signed-off-by: Masakazu Asama <masakazu.asama@gmail.com>
---
include/net/ip6_tunnel.h | 18 ++
include/uapi/linux/if_tunnel.h | 18 ++
net/ipv6/ip6_tunnel.c | 491 ++++++++++++++++++++++++++++++++-
3 files changed, 525 insertions(+), 2 deletions(-)
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index 69b4bcf880c9..daa159185228 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -18,6 +18,16 @@
/* determine capability on a per-packet basis */
#define IP6_TNL_F_CAP_PER_PACKET 0x40000
+struct ip6_tnl_rule {
+ u8 version;
+ struct in6_addr ipv6_subnet;
+ u8 ipv6_prefixlen;
+ struct in_addr ipv4_subnet;
+ u8 ipv4_prefixlen;
+ u8 ea_length;
+ u8 psid_offset;
+};
+
struct __ip6_tnl_parm {
char name[IFNAMSIZ]; /* name of tunnel device */
int link; /* ifindex of underlying L2 interface */
@@ -40,6 +50,13 @@ struct __ip6_tnl_parm {
__u8 erspan_ver; /* ERSPAN version */
__u8 dir; /* direction */
__u16 hwid; /* hwid */
+ __u8 rule_action;
+ struct ip6_tnl_rule rule;
+};
+
+struct ip6_rule_list {
+ struct list_head list;
+ struct ip6_tnl_rule data;
};
/* IPv6 tunnel */
@@ -63,6 +80,7 @@ struct ip6_tnl {
int encap_hlen; /* Encap header length (FOU,GUE) */
struct ip_tunnel_encap encap;
int mlink;
+ struct ip6_rule_list rules;
};
struct ip6_tnl_encap_ops {
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
index 1b3d148c4560..7cb09c8c4d8a 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -77,10 +77,28 @@ enum {
IFLA_IPTUN_ENCAP_DPORT,
IFLA_IPTUN_COLLECT_METADATA,
IFLA_IPTUN_FWMARK,
+ IFLA_IPTUN_RULE_VERSION,
+ IFLA_IPTUN_RULE_ACTION,
+ IFLA_IPTUN_RULE_IPV6_PREFIX,
+ IFLA_IPTUN_RULE_IPV6_PREFIXLEN,
+ IFLA_IPTUN_RULE_IPV4_PREFIX,
+ IFLA_IPTUN_RULE_IPV4_PREFIXLEN,
+ IFLA_IPTUN_RULE_EA_LENGTH,
+ IFLA_IPTUN_RULE_PSID_OFFSET,
__IFLA_IPTUN_MAX,
};
#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
+enum map_rule_versions {
+ MAP_VERSION_RFC,
+ MAP_VERSION_DRAFT3,
+};
+
+enum tunnel_rule_actions {
+ TUNNEL_RULE_ACTION_ADD = 1,
+ TUNNEL_RULE_ACTION_DELETE = 2,
+};
+
enum tunnel_encap_types {
TUNNEL_ENCAP_NONE,
TUNNEL_ENCAP_FOU,
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index a9d06d4dd057..e13dc5bd09b5 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -20,6 +20,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/list.h>
+#include <linux/list_sort.h>
#include <linux/module.h>
#include <linux/capability.h>
#include <linux/errno.h>
@@ -32,6 +34,7 @@
#include <linux/net.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <linux/icmpv6.h>
#include <linux/init.h>
@@ -124,6 +127,228 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)
return &dev->stats;
}
+int
+ip6_get_addrport(struct iphdr *iph, __be32 *saddr4, __be32 *daddr4,
+ __be16 *sport4, __be16 *dport4, __u8 *proto, int *icmperr)
+{
+ u8 *ptr;
+ struct iphdr *icmpiph = NULL;
+ struct tcphdr *tcph, *icmptcph;
+ struct udphdr *udph, *icmpudph;
+ struct icmphdr *icmph, *icmpicmph;
+
+ *icmperr = 0;
+ *saddr4 = iph->saddr;
+ *daddr4 = iph->daddr;
+ ptr = (u8 *)iph;
+ ptr += iph->ihl * 4;
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ *proto = IPPROTO_TCP;
+ tcph = (struct tcphdr *)ptr;
+ *sport4 = tcph->source;
+ *dport4 = tcph->dest;
+ break;
+ case IPPROTO_UDP:
+ *proto = IPPROTO_UDP;
+ udph = (struct udphdr *)ptr;
+ *sport4 = udph->source;
+ *dport4 = udph->dest;
+ break;
+ case IPPROTO_ICMP:
+ *proto = IPPROTO_ICMP;
+ icmph = (struct icmphdr *)ptr;
+ switch (icmph->type) {
+ case ICMP_DEST_UNREACH:
+ case ICMP_SOURCE_QUENCH:
+ case ICMP_TIME_EXCEEDED:
+ case ICMP_PARAMETERPROB:
+ *icmperr = 1;
+ ptr = (u8 *)icmph;
+ ptr += sizeof(struct icmphdr);
+ icmpiph = (struct iphdr *)ptr;
+ *saddr4 = icmpiph->saddr;
+ *daddr4 = icmpiph->daddr;
+ if (ntohs(iph->tot_len) < icmpiph->ihl * 4 + 12)
+ return -1;
+ ptr += icmpiph->ihl * 4;
+ switch (icmpiph->protocol) {
+ case IPPROTO_TCP:
+ *proto = IPPROTO_TCP;
+ icmptcph = (struct tcphdr *)ptr;
+ *sport4 = icmptcph->source;
+ *dport4 = icmptcph->dest;
+ break;
+ case IPPROTO_UDP:
+ *proto = IPPROTO_UDP;
+ icmpudph = (struct udphdr *)ptr;
+ *sport4 = icmpudph->source;
+ *dport4 = icmpudph->dest;
+ break;
+ case IPPROTO_ICMP:
+ *proto = IPPROTO_ICMP;
+ icmpicmph = (struct icmphdr *)ptr;
+ *sport4 = icmpicmph->un.echo.id;
+ *dport4 = icmpicmph->un.echo.id;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ *sport4 = icmph->un.echo.id;
+ *dport4 = icmph->un.echo.id;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ip6_mape_gen_addr6(struct ip6_tnl_rule *mr, struct in6_addr *addr6,
+ __be32 addr4, __be16 port4)
+{
+ int i, pbw0, pbi0, pbi1;
+ __u32 addr[4];
+ __u32 psid = 0;
+ __u32 mask = 0;
+ __u32 a = ntohl(addr4);
+ __u16 p = ntohs(port4);
+ int psid_prefix_length = 0;
+ int psid_mask;
+ __u32 id0 = 0;
+ __u32 id1 = 0;
+
+ /* If we have no rule then we can return immediately */
+ if (!mr)
+ return -1;
+
+ psid_prefix_length = mr->ea_length - (32 - mr->ipv4_prefixlen);
+
+ /* In some configurations the PSID can overlap the IPv4 address
+ * segment
+ */
+ if (psid_prefix_length < 0)
+ a &= 0xffffffff << (psid_prefix_length * -1);
+
+ /* Find the PSID from the port by shifting the non-PSID bits out */
+ if (psid_prefix_length > 0) {
+ mask = 0xffffffff >> (32 - psid_prefix_length);
+ psid =
+ (p >> (16 - psid_prefix_length - mr->psid_offset)) & mask;
+ }
+
+ /* We start with the IPv6 subnet provided for this rule */
+ for (i = 0; i < 4; ++i)
+ addr[i] = ntohl(mr->ipv6_subnet.s6_addr32[i]);
+
+ /* We then copy in the IPv4 host portion - pbw0 is the starting
+ * 32 bit chunk, pbi0 is the offset into that chunk
+ */
+ if (mr->ipv4_prefixlen < 32) {
+ pbw0 = mr->ipv6_prefixlen >> 5;
+ pbi0 = mr->ipv6_prefixlen & 0x1f;
+ addr[pbw0] |= (a << mr->ipv4_prefixlen) >> pbi0;
+ pbi1 = pbi0 - mr->ipv4_prefixlen;
+ if (pbi1 > 0)
+ addr[pbw0 + 1] |= a << (32 - pbi1);
+ }
+
+ /* The PSID may need to be inserted into the IPv6 address within the
+ * top 64 bits. Again: pbw0 is the chunk, pbi0 is the offset.
+ */
+ if ((psid_prefix_length - mr->psid_offset) > 0) {
+ psid_mask = mask;
+ pbw0 = (mr->ipv6_prefixlen + (32 - mr->ipv4_prefixlen)) >> 5;
+ pbi0 = (mr->ipv6_prefixlen + (32 - mr->ipv4_prefixlen)) & 0x1f;
+
+ pbi1 = pbi0 - (32 - psid_prefix_length);
+
+ if (pbi1 > 0) {
+ addr[pbw0] |= (psid >> pbi1);
+ addr[pbw0 + 1] |= (psid & psid_mask) << (32 - pbi1);
+ } else {
+ addr[pbw0] |= psid << (32 - pbi0 - psid_prefix_length);
+ }
+ }
+
+ /* The format of the interface id changes depending on version */
+ if (mr->version == MAP_VERSION_RFC) {
+ addr[2] |= (a >> 16);
+ addr[3] |= (a << 16);
+ addr[3] |= psid;
+ } else if (mr->version == MAP_VERSION_DRAFT3) {
+ id0 |= a >> 8;
+ id1 |= (a & 0xFF) << 24;
+ id1 |= psid << 8;
+ addr[2] |= id0;
+ addr[3] |= id1;
+ } else {
+ return -1;
+ }
+
+ /* Copy the formed address back into network order */
+ for (i = 0; i < 4; ++i)
+ addr6->s6_addr32[i] = htonl(addr[i]);
+
+ return 0;
+}
+
+static struct ip6_tnl_rule *ip6_tnl_rule_find(struct net_device *dev,
+ __be32 _dst)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *pos = NULL;
+ u32 dst = ntohl(_dst);
+
+ list_for_each_entry(pos, &t->rules.list, list) {
+ int mask =
+ 0xFFFFFFFF ^ ((1 << (32 - pos->data.ipv4_prefixlen)) - 1);
+ if ((dst & mask) == ntohl(pos->data.ipv4_subnet.s_addr))
+ return &pos->data;
+ }
+ return NULL;
+}
+
+static void
+ip6_tnl_mape_dst(struct net_device *dev, struct sk_buff *skb,
+ struct flowi6 *fl6)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct iphdr *iph;
+ __be32 saddr4, daddr4, addr;
+ __be16 sport4, dport4, port;
+ __u8 proto;
+ int icmperr;
+ struct ip6_tnl_rule *mr = NULL;
+
+ /* If we have no rules then packets always go to the tunnel dst */
+ if (list_empty(&t->rules.list))
+ return;
+
+ /* Find the src / dst address / port (or ID for ICMP) */
+ iph = ip_hdr(skb);
+ ip6_get_addrport(iph, &saddr4, &daddr4, &sport4, &dport4, &proto,
+ &icmperr);
+
+ if (icmperr) {
+ mr = ip6_tnl_rule_find(dev, saddr4);
+ addr = saddr4;
+ port = sport4;
+ } else {
+ mr = ip6_tnl_rule_find(dev, daddr4);
+ addr = daddr4;
+ port = dport4;
+ }
+
+ /* Generate an destination IPv6 address from the internal packet */
+ if (mr)
+ ip6_mape_gen_addr6(mr, &fl6->daddr, addr, port);
+}
+
/**
* ip6_tnl_lookup - fetch tunnel matching the end-point addresses
* @remote: the address of the tunnel exit-point
@@ -207,6 +432,33 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
return &ip6n->tnls[prio][h];
}
+/**
+ * ip6_tnl_bucket_r_any - get head of list matching given tunnel parameters
+ * @p: parameters containing tunnel end-points
+ *
+ * Description:
+ * ip6_tnl_bucket_r_any() returns the head of the list matching the
+ * &struct in6_addr entry laddr in @p.
+ *
+ * Return: head of IPv6 tunnel list
+ **/
+
+static struct ip6_tnl __rcu **
+ip6_tnl_bucket_r_any(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
+{
+ const struct in6_addr *local = &p->laddr;
+ unsigned int h = 0;
+ int prio = 0;
+ struct in6_addr any;
+
+ if (!ipv6_addr_any(local)) {
+ memset(&any, 0, sizeof(any));
+ prio = 1;
+ h = HASH(&any, local);
+ }
+ return &ip6n->tnls[prio][h];
+}
+
/**
* ip6_tnl_link - add tunnel to hash table
* @t: tunnel to be added
@@ -215,7 +467,12 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
static void
ip6_tnl_link(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
{
- struct ip6_tnl __rcu **tp = ip6_tnl_bucket(ip6n, &t->parms);
+ struct ip6_tnl __rcu **tp;
+
+ if (!list_empty(&t->rules.list))
+ tp = ip6_tnl_bucket_r_any(ip6n, &t->parms);
+ else
+ tp = ip6_tnl_bucket(ip6n, &t->parms);
if (t->parms.collect_md)
rcu_assign_pointer(ip6n->collect_md_tun, t);
@@ -234,6 +491,15 @@ ip6_tnl_unlink(struct ip6_tnl_net *ip6n, struct ip6_tnl *t)
struct ip6_tnl __rcu **tp;
struct ip6_tnl *iter;
+ for (tp = ip6_tnl_bucket_r_any(ip6n, &t->parms);
+ (iter = rtnl_dereference(*tp)) != NULL;
+ tp = &iter->next) {
+ if (t == iter) {
+ rcu_assign_pointer(*tp, t->next);
+ return;
+ }
+ }
+
if (t->parms.collect_md)
rcu_assign_pointer(ip6n->collect_md_tun, NULL);
@@ -352,6 +618,16 @@ static struct ip6_tnl *ip6_tnl_locate(struct net *net,
struct ip6_tnl *t;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ for (tp = ip6_tnl_bucket_r_any(ip6n, p);
+ (t = rtnl_dereference(*tp)) != NULL;
+ tp = &t->next) {
+ if (ipv6_addr_equal(local, &t->parms.laddr)) {
+ if (create)
+ return ERR_PTR(-EEXIST);
+ return t;
+ }
+ }
+
for (tp = ip6_tnl_bucket(ip6n, p);
(t = rtnl_dereference(*tp)) != NULL;
tp = &t->next) {
@@ -880,6 +1156,64 @@ static const struct tnl_ptk_info tpi_v4 = {
.proto = htons(ETH_P_IP),
};
+static int ip6_tnl_ipv4_addr_match(struct ip6_tnl *t, __be32 addr)
+{
+ struct in_device *ind;
+ int ret = 0;
+
+ ind = in_dev_get(t->dev);
+ if (!ind)
+ return 0;
+
+ for_ifa(ind) {
+ if (ifa->ifa_address == addr) {
+ ret = 1;
+ break;
+ }
+ }
+ endfor_ifa(ind);
+ in_dev_put(ind);
+
+ return ret;
+}
+
+static int ip6_tnl_map_rule_validate(struct ip6_tnl *t, struct sk_buff *skb)
+{
+ struct ipv6hdr *ipv6h;
+ struct iphdr *iph;
+ __be32 src, dst;
+ __be16 sport, dport;
+ __u8 proto;
+ int icmperr;
+ struct ip6_tnl_rule *mr = NULL;
+ struct in6_addr src6 = { };
+
+ if (likely(list_empty(&t->rules.list)))
+ return 1;
+
+ iph = ipip_hdr(skb);
+ ip6_get_addrport(iph, &src, &dst, &sport, &dport, &proto, &icmperr);
+
+ /* Check if the destination of the packet is for the MAP-E tunnel. */
+ if (!ip6_tnl_ipv4_addr_match(t, dst))
+ return 0;
+
+ /* if the packet's destination is for the tunnel, we need to check
+ * if the source ipv4 address matches any map rules. If there is
+ * no matching rule, just return success (the packet is from br).
+ * Otherwise, we check if the source ipv6 address is valid.
+ */
+ mr = ip6_tnl_rule_find(t->dev, src);
+ if (mr) {
+ ip6_mape_gen_addr6(mr, &src6, src, sport);
+ ipv6h = ipv6_hdr(skb);
+ if (!ipv6_addr_equal(&src6, &ipv6h->saddr))
+ return 0;
+ }
+
+ return 1;
+}
+
static int ipxip6_rcv(struct sk_buff *skb, u8 ipproto,
const struct tnl_ptk_info *tpi,
int (*dscp_ecn_decapsulate)(const struct ip6_tnl *t,
@@ -905,6 +1239,8 @@ static int ipxip6_rcv(struct sk_buff *skb, u8 ipproto,
goto drop;
if (iptunnel_pull_header(skb, 0, tpi->proto, false))
goto drop;
+ if (!ip6_tnl_map_rule_validate(t, skb))
+ goto drop;
if (t->parms.collect_md) {
tun_dst = ipv6_tun_rx_dst(skb, 0, 0, 0);
if (!tun_dst)
@@ -1092,6 +1428,12 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
use_cache = true;
}
+ /* In MAP-E mode we need to always do a route lookup */
+ if (!list_empty(&t->rules.list)) {
+ use_cache = false;
+ ip6_tnl_mape_dst(dev, skb, fl6);
+ }
+
if (use_cache)
dst = dst_cache_get(&t->dst_cache);
@@ -1863,6 +2205,8 @@ ip6_tnl_dev_init_gen(struct net_device *dev)
dev->min_mtu = ETH_MIN_MTU;
dev->max_mtu = IP6_MAX_MTU - dev->hard_header_len;
+ INIT_LIST_HEAD(&t->rules.list);
+
return 0;
destroy_dst:
@@ -1968,6 +2312,95 @@ static void ip6_tnl_netlink_parms(struct nlattr *data[],
if (data[IFLA_IPTUN_FWMARK])
parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]);
+
+ if (data[IFLA_IPTUN_RULE_ACTION])
+ parms->rule_action = nla_get_u8(data[IFLA_IPTUN_RULE_ACTION]);
+
+ if (data[IFLA_IPTUN_RULE_VERSION])
+ parms->rule.version = nla_get_u8(data[IFLA_IPTUN_RULE_VERSION]);
+
+ if (data[IFLA_IPTUN_RULE_IPV6_PREFIX])
+ parms->rule.ipv6_subnet =
+ nla_get_in6_addr(data[IFLA_IPTUN_RULE_IPV6_PREFIX]);
+
+ if (data[IFLA_IPTUN_RULE_IPV6_PREFIXLEN])
+ parms->rule.ipv6_prefixlen =
+ nla_get_u8(data[IFLA_IPTUN_RULE_IPV6_PREFIXLEN]);
+
+ if (data[IFLA_IPTUN_RULE_IPV4_PREFIX])
+ parms->rule.ipv4_subnet.s_addr =
+ nla_get_in_addr(data[IFLA_IPTUN_RULE_IPV4_PREFIX]);
+
+ if (data[IFLA_IPTUN_RULE_IPV4_PREFIXLEN])
+ parms->rule.ipv4_prefixlen =
+ nla_get_u8(data[IFLA_IPTUN_RULE_IPV4_PREFIXLEN]);
+
+ if (data[IFLA_IPTUN_RULE_EA_LENGTH])
+ parms->rule.ea_length =
+ nla_get_u8(data[IFLA_IPTUN_RULE_EA_LENGTH]);
+
+ if (data[IFLA_IPTUN_RULE_PSID_OFFSET])
+ parms->rule.psid_offset =
+ nla_get_u8(data[IFLA_IPTUN_RULE_PSID_OFFSET]);
+}
+
+static int ip6_tnl_rule_cmp(void *unused, struct list_head *_a,
+ struct list_head *_b)
+{
+ struct ip6_rule_list *a = list_entry(_a, struct ip6_rule_list, list);
+ struct ip6_rule_list *b = list_entry(_b, struct ip6_rule_list, list);
+ int diff;
+
+ diff = b->data.ipv4_prefixlen - a->data.ipv4_prefixlen;
+ if (!diff)
+ diff = b->data.ipv4_subnet.s_addr - a->data.ipv4_subnet.s_addr;
+
+ if (!diff)
+ diff = b->data.ipv6_prefixlen - a->data.ipv6_prefixlen;
+
+ if (!diff)
+ diff =
+ memcmp(&b->data.ipv6_subnet, &a->data.ipv6_subnet,
+ sizeof(b->data.ipv6_subnet));
+
+ if (!diff)
+ diff = b->data.ea_length - a->data.ea_length;
+
+ if (!diff)
+ diff = b->data.psid_offset - a->data.psid_offset;
+
+ return diff;
+}
+
+static void ip6_tnl_add_rule(struct net_device *dev,
+ struct __ip6_tnl_parm *new_cfg)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *new_rule = kmalloc(sizeof(t->rules), GFP_KERNEL);
+
+ if (!new_rule)
+ return;
+
+ memcpy(&new_rule->data, &new_cfg->rule, sizeof(new_rule->data));
+ INIT_LIST_HEAD(&new_rule->list);
+ list_add(&new_rule->list, &t->rules.list);
+ list_sort(NULL, &t->rules.list, ip6_tnl_rule_cmp);
+}
+
+static void ip6_tnl_delete_rule(struct net_device *dev,
+ struct __ip6_tnl_parm *_old_rule)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *pos = NULL;
+ struct ip6_rule_list old_rule = { .data = _old_rule->rule };
+ struct ip6_rule_list *iter = NULL;
+
+ list_for_each_entry_safe(pos, iter, &t->rules.list, list) {
+ if (ip6_tnl_rule_cmp(NULL, &pos->list, &old_rule.list) == 0) {
+ list_del(&pos->list);
+ break;
+ }
+ }
}
static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[],
@@ -2035,6 +2468,16 @@ static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev,
err = ip6_tnl_create2(dev);
if (!err && tb[IFLA_MTU])
ip6_tnl_change_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
+ if (!err && data[IFLA_IPTUN_RULE_ACTION]) {
+ switch (nla_get_u8(data[IFLA_IPTUN_RULE_ACTION])) {
+ case TUNNEL_RULE_ACTION_ADD:
+ ip6_tnl_add_rule(dev, &nt->parms);
+ break;
+ case TUNNEL_RULE_ACTION_DELETE:
+ ip6_tnl_delete_rule(dev, &nt->parms);
+ break;
+ }
+ }
return err;
}
@@ -2048,6 +2491,7 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
struct net *net = t->net;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
struct ip_tunnel_encap ipencap;
+ int err;
if (dev == ip6n->fb_tnl_dev)
return -EINVAL;
@@ -2069,13 +2513,32 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
} else
t = netdev_priv(dev);
- return ip6_tnl_update(t, &p);
+ err = ip6_tnl_update(t, &p);
+ if (!err && data[IFLA_IPTUN_RULE_ACTION]) {
+ switch (nla_get_u8(data[IFLA_IPTUN_RULE_ACTION])) {
+ case TUNNEL_RULE_ACTION_ADD:
+ ip6_tnl_add_rule(dev, &p);
+ break;
+ case TUNNEL_RULE_ACTION_DELETE:
+ ip6_tnl_delete_rule(dev, &p);
+ break;
+ }
+ }
+
+ return err;
}
static void ip6_tnl_dellink(struct net_device *dev, struct list_head *head)
{
struct net *net = dev_net(dev);
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ip6_rule_list *pos = NULL;
+ struct ip6_rule_list *iter = NULL;
+
+ list_for_each_entry_safe(pos, iter, &t->rules.list, list) {
+ list_del(&pos->list);
+ }
if (dev != ip6n->fb_tnl_dev)
unregister_netdevice_queue(dev, head);
@@ -2112,6 +2575,22 @@ static size_t ip6_tnl_get_size(const struct net_device *dev)
nla_total_size(0) +
/* IFLA_IPTUN_FWMARK */
nla_total_size(4) +
+ /* IFLA_IPTUN_RULE_VERSION */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_ACTION */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_IPV6_PREFIXLEN */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_IPV4_PREFIXLEN */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_EA_LENGTH */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_PSID_OFFSET */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_RULE_IPV6_PREFIX */
+ nla_total_size(sizeof(struct in6_addr)) +
+ /* IFLA_IPTUN_RULE_IPV4_PREFIX */
+ nla_total_size(sizeof(struct in6_addr)) +
0;
}
@@ -2170,6 +2649,14 @@ static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = {
[IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 },
[IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG },
[IFLA_IPTUN_FWMARK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_RULE_VERSION] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_ACTION] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_IPV6_PREFIXLEN] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_IPV4_PREFIXLEN] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_EA_LENGTH] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_PSID_OFFSET] = { .type = NLA_U8 },
+ [IFLA_IPTUN_RULE_IPV6_PREFIX] = { .len = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_RULE_IPV4_PREFIX] = { .len = sizeof(struct in_addr) },
};
static struct rtnl_link_ops ip6_link_ops __read_mostly = {
--
2.19.1
^ permalink raw reply related
* [PATCH net-next] net_sched: sch_fq: avoid calling ktime_get_ns() if not needed
From: Eric Dumazet @ 2018-11-20 1:30 UTC (permalink / raw)
To: David S . Miller; +Cc: netdev, Eric Dumazet, Eric Dumazet
There are two cases were we can avoid calling ktime_get_ns() :
1) Queue is empty.
2) Internal queue is not empty.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/sched/sch_fq.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index 1da8864502d448cfa93a9c02da504af61a1093e5..1a662f2bb7bb7bb5507107f61657d44fa28ca991 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -414,16 +414,21 @@ static void fq_check_throttled(struct fq_sched_data *q, u64 now)
static struct sk_buff *fq_dequeue(struct Qdisc *sch)
{
struct fq_sched_data *q = qdisc_priv(sch);
- u64 now = ktime_get_ns();
struct fq_flow_head *head;
struct sk_buff *skb;
struct fq_flow *f;
unsigned long rate;
u32 plen;
+ u64 now;
+
+ if (!sch->q.qlen)
+ return NULL;
skb = fq_dequeue_head(sch, &q->internal);
if (skb)
goto out;
+
+ now = ktime_get_ns();
fq_check_throttled(q, now);
begin:
head = &q->new_flows;
--
2.19.1.1215.g8438c0b245-goog
^ permalink raw reply related
* [PATCH iproute2] bpf: initialise map symbol before retrieving and comparing its type
From: Quentin Monnet @ 2018-11-20 1:26 UTC (permalink / raw)
To: David Ahern, Stephen Hemminger
Cc: Yonghong Song, Alexei Starovoitov, Daniel Borkmann, netdev,
oss-drivers, Quentin Monnet
In order to compare BPF map symbol type correctly in regard to the
latest LLVM, commit 7a04dd84a7f9 ("bpf: check map symbol type properly
with newer llvm compiler") compares map symbol type to both NOTYPE and
OBJECT. To do so, it first retrieves the type from "sym.st_info" and
stores it into a temporary variable.
However, the type is collected from the symbol "sym" before this latter
symbol is actually updated. gelf_getsym() is called after that and
updates "sym", and when comparison with OBJECT or NOTYPE happens it is
done on the type of the symbol collected in the previous passage of the
loop (or on an uninitialised symbol on the first passage). This may
eventually break map collection from the ELF file.
Fix this by assigning the type to the temporary variable only after the
call to gelf_getsym().
Fixes: 7a04dd84a7f9 ("bpf: check map symbol type properly with newer llvm compiler")
Reported-by: Ron Philip <ron.philip@netronome.com>
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jiong Wang <jiong.wang@netronome.com>
---
lib/bpf.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/lib/bpf.c b/lib/bpf.c
index 45f279fa4a41..6aff8f7bad7f 100644
--- a/lib/bpf.c
+++ b/lib/bpf.c
@@ -1758,11 +1758,12 @@ static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which)
int i;
for (i = 0; i < ctx->sym_num; i++) {
- int type = GELF_ST_TYPE(sym.st_info);
+ int type;
if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
continue;
+ type = GELF_ST_TYPE(sym.st_info);
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
(type != STT_NOTYPE && type != STT_OBJECT) ||
sym.st_shndx != ctx->sec_maps ||
@@ -1851,11 +1852,12 @@ static int bpf_map_num_sym(struct bpf_elf_ctx *ctx)
GElf_Sym sym;
for (i = 0; i < ctx->sym_num; i++) {
- int type = GELF_ST_TYPE(sym.st_info);
+ int type;
if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
continue;
+ type = GELF_ST_TYPE(sym.st_info);
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
(type != STT_NOTYPE && type != STT_OBJECT) ||
sym.st_shndx != ctx->sec_maps)
@@ -1931,10 +1933,12 @@ static int bpf_map_verify_all_offs(struct bpf_elf_ctx *ctx, int end)
* the table again.
*/
for (i = 0; i < ctx->sym_num; i++) {
- int type = GELF_ST_TYPE(sym.st_info);
+ int type;
if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
continue;
+
+ type = GELF_ST_TYPE(sym.st_info);
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
(type != STT_NOTYPE && type != STT_OBJECT) ||
sym.st_shndx != ctx->sec_maps)
--
2.7.4
^ permalink raw reply related
* [PATCH v3 0/5] phy: core: rework phy_set_mode to accept phy mode and submode
From: Grygorii Strashko @ 2018-11-20 1:24 UTC (permalink / raw)
To: David S. Miller, Kishon Vijay Abraham I, Antoine Tenart,
Quentin Schulz, Russell King - ARM Linux, Maxime Chevallier
Cc: netdev, Sekhar Nori, linux-kernel, linux-arm-kernel,
Tony Lindgren, linux-amlogic, linux-mediatek, Alexandre Belloni,
Vivek Gautam, Maxime Ripard, Chen-Yu Tsai, Carlo Caione,
Chunfeng Yun, Matthias Brugger, Manu Gautam, Grygorii Strashko
Hi Kishon, All,
Thank you for your review.
I've not added "Tested-by"/"Acked-by" tags due to code changes in v3.
As was discussed in [1] I'm posting series which introduces rework of
phy_set_mode to accept phy mode and submode. I've dropped TI specific patches as
this change is pretty big by itself.
Patch 1 is cumulative change which refactors PHY framework code to
support dual level PHYs mode configuration - PHY mode and PHY submode. It
extends .set_mode() callback to support additional parameter "int submode"
and converts all corresponding PHY drivers to support new .set_mode()
callback declaration.
The new extended PHY API
int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
is introduced to support dual level PHYs mode configuration and existing
phy_set_mode() API is converted to macros, so PHY framework consumers do
not need to be changed (~21 matches).
Patches 2-4: Add new PHY's mode to be used by Ethernet PHY interface drivers or
multipurpose PHYs like serdes and convert ocelot-serdes and mvebu-cp110-comphy
PHY drivers to use recently introduced PHY_MODE_ETHERNET and phy_set_mode_ext().
Patch 5 - removes unused, ethernet specific phy modes from enum phy_mode.
Testing:
- series tested on TI am335x/am437x/am5(dra7) paltforms.
- other driver build tested.
changes in v3:
- mux tables for PHY ocelot-serdes driver updated to store PHY mode and submode
- mux tables for PHY mvebu-cp110-comphy driver updated to store PHY mode
and submode
changes in v2:
- marvell PHY and net drivers updated as recommended by Russell King
v2: https://lkml.org/lkml/2018/11/10/220
v1: https://lkml.org/lkml/2018/11/8/260
[1] https://lkml.org/lkml/2018/10/25/366
Grygorii Strashko (5):
phy: core: rework phy_set_mode to accept phy mode and submode
phy: core: add PHY_MODE_ETHERNET
phy: ocelot-serdes: convert to use eth phy mode and submode
phy: mvebu-cp110-comphy: convert to use eth phy mode and submode
phy: core: clean up unused ethernet specific phy modes
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 +----
drivers/net/ethernet/mscc/ocelot.c | 9 +--
drivers/phy/allwinner/phy-sun4i-usb.c | 3 +-
drivers/phy/amlogic/phy-meson-gxl-usb2.c | 5 +-
drivers/phy/amlogic/phy-meson-gxl-usb3.c | 5 +-
drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 93 ++++++++++++++-----------
drivers/phy/mediatek/phy-mtk-tphy.c | 2 +-
drivers/phy/mediatek/phy-mtk-xsphy.c | 2 +-
drivers/phy/mscc/phy-ocelot-serdes.c | 24 +++++--
drivers/phy/phy-core.c | 6 +-
drivers/phy/qualcomm/phy-qcom-qmp.c | 3 +-
drivers/phy/qualcomm/phy-qcom-qusb2.c | 3 +-
drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 3 +-
drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 3 +-
drivers/phy/qualcomm/phy-qcom-usb-hs.c | 3 +-
drivers/phy/ti/phy-da8xx-usb.c | 3 +-
drivers/phy/ti/phy-tusb1210.c | 2 +-
include/linux/phy/phy.h | 18 +++--
18 files changed, 111 insertions(+), 95 deletions(-)
--
2.10.5
^ permalink raw reply
* Re: [PATCH bpf-next] bpf: libbpf: retry program creation without the name
From: Y Song @ 2018-11-20 1:24 UTC (permalink / raw)
To: sdf; +Cc: netdev, Daniel Borkmann, Alexei Starovoitov, vladum
In-Reply-To: <20181120004625.243494-1-sdf@google.com>
On Mon, Nov 19, 2018 at 4:52 PM Stanislav Fomichev <sdf@google.com> wrote:
>
> [Recent commit 23499442c319 ("bpf: libbpf: retry map creation without
> the name") fixed this issue for maps, let's do the same for programs.]
>
> Since commit 88cda1c9da02 ("bpf: libbpf: Provide basic API support
> to specify BPF obj name"), libbpf unconditionally sets bpf_attr->name
> for programs. Pre v4.14 kernels don't know about programs names and
> return an error about unexpected non-zero data. Retry sys_bpf without
> a program name to cover older kernels.
>
> Signed-off-by: Stanislav Fomichev <sdf@google.com>
Acked-by: Yonghong Song <yhs@fb.com>
^ permalink raw reply
* Re: [PATCH bpf-next 2/2] bpf: adding tests for mapinmap helpber in libbpf
From: Y Song @ 2018-11-20 1:18 UTC (permalink / raw)
To: tehnerd; +Cc: Alexei Starovoitov, Daniel Borkmann, Jakub Kicinski, netdev
In-Reply-To: <20181120000626.16825-3-tehnerd@tehnerd.com>
On Mon, Nov 19, 2018 at 4:13 PM Nikita V. Shirokov <tehnerd@tehnerd.com> wrote:
>
> adding test/example of bpf_map__add_inner_map_fd usage
>
> Signed-off-by: Nikita V. Shirokov <tehnerd@tehnerd.com>
> ---
> tools/testing/selftests/bpf/Makefile | 3 +-
> tools/testing/selftests/bpf/test_mapinmap.c | 53 ++++++++++++++++++++
> tools/testing/selftests/bpf/test_maps.c | 76 +++++++++++++++++++++++++++++
> 3 files changed, 131 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/bpf/test_mapinmap.c
>
> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
> index 57b4712a6276..a3ea69dc9bdf 100644
> --- a/tools/testing/selftests/bpf/Makefile
> +++ b/tools/testing/selftests/bpf/Makefile
> @@ -38,7 +38,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
> test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
> get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \
> test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o \
> - test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o
> + test_sk_lookup_kern.o test_xdp_vlan.o test_queue_map.o test_stack_map.o \
> + test_mapinmap.o
>
> # Order correspond to 'make run_tests' order
> TEST_PROGS := test_kmod.sh \
> diff --git a/tools/testing/selftests/bpf/test_mapinmap.c b/tools/testing/selftests/bpf/test_mapinmap.c
> new file mode 100644
> index 000000000000..8aef6c652c9c
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/test_mapinmap.c
> @@ -0,0 +1,53 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2018 Facebook */
> +#include <stddef.h>
> +#include <linux/bpf.h>
> +#include <linux/types.h>
> +#include "bpf_helpers.h"
> +
> +
nit: extra new line
> +struct bpf_map_def SEC("maps") mim_array = {
> + .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
> + .key_size = sizeof(int),
> + /* must be sizeof(__u32) for map in map */
> + .value_size = sizeof(__u32),
> + .max_entries = 1,
> + .map_flags = 0,
> +};
> +
> +struct bpf_map_def SEC("maps") mim_hash = {
> + .type = BPF_MAP_TYPE_HASH_OF_MAPS,
> + .key_size = sizeof(int),
> + /* must be sizeof(__u32) for map in map */
> + .value_size = sizeof(__u32),
> + .max_entries = 1,
> + .map_flags = 0,
> +};
> +
> +
> +
nit: extra new lines.
> +SEC("xdp_mimtest")
> +int xdp_mimtest0(struct xdp_md *ctx)
> +{
> + int value = 123;
> + int key = 0;
> + void *map;
> +
> + map = bpf_map_lookup_elem(&mim_array, &key);
> + if (!map)
> + return XDP_DROP;
> +
> + bpf_map_update_elem(map, &key, &value, 0);
> +
> + map = bpf_map_lookup_elem(&mim_hash, &key);
> + if (!map)
> + return XDP_DROP;
> +
> + bpf_map_update_elem(map, &key, &value, 0);
> +
> + return XDP_PASS;
> +}
> +
> +
nit: extra new line
> +int _version SEC("version") = 1;
> +char _license[] SEC("license") = "GPL";
> diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c
> index 4db2116e52be..a49ab294971d 100644
> --- a/tools/testing/selftests/bpf/test_maps.c
> +++ b/tools/testing/selftests/bpf/test_maps.c
> @@ -1080,6 +1080,80 @@ static void test_sockmap(int tasks, void *data)
> exit(1);
> }
>
> +#define MAPINMAP_PROG "./test_mapinmap.o"
> +static void test_mapinmap(void)
> +{
> + struct bpf_program *prog;
> + struct bpf_object *obj;
> + struct bpf_map *map;
> + int mim_fd, fd;
> + int pos = 0;
> +
> + obj = bpf_object__open(MAPINMAP_PROG);
> +
> + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int), sizeof(int),
> + 2, 0);
> + if (fd < 0) {
> + printf("Failed to create hashmap '%s'!\n", strerror(errno));
> + exit(1);
> + }
> + printf("fd is %d\n", fd);
not sure whether you need to print fd in the console or not. Any particular
reason?
> +
> + map = bpf_object__find_map_by_name(obj, "mim_array");
> + if (IS_ERR(map)) {
> + printf("Failed to load array of maps from test prog\n");
> + goto out_mapinmap;
> + }
> + bpf_map__add_inner_map_fd(map, fd);
> +
> + map = bpf_object__find_map_by_name(obj, "mim_hash");
> + if (IS_ERR(map)) {
> + printf("Failed to load hash of maps from test prog\n");
> + goto out_mapinmap;
> + }
> + bpf_map__add_inner_map_fd(map, fd);
> +
> +
nit: extra new line
> + bpf_object__for_each_program(prog, obj) {
> + bpf_program__set_xdp(prog);
> + }
> + bpf_object__load(obj);
> +
> + map = bpf_object__find_map_by_name(obj, "mim_array");
> + if (IS_ERR(map)) {
> + printf("Failed to load array of maps from test prog\n");
> + goto out_mapinmap;
> + }
> + mim_fd = bpf_map__fd(map);
> + if (mim_fd < 0) {
> + printf("Failed to get descriptor for array of maps\n");
> + goto out_mapinmap;
> + }
> +
> + bpf_map_update_elem(mim_fd, &pos, &fd, 0);
do you want to test return value of bpf_map_update_elem() to make sure
update is successful?
> +
> + map = bpf_object__find_map_by_name(obj, "mim_hash");
> + if (IS_ERR(map)) {
> + printf("Failed to load hash of maps from test prog\n");
> + goto out_mapinmap;
> + }
> + mim_fd = bpf_map__fd(map);
> + if (mim_fd < 0) {
> + printf("Failed to get descriptor for hash of maps\n");
> + goto out_mapinmap;
> + }
> + bpf_map_update_elem(mim_fd, &pos, &fd, 0);
do you want to test return value of bpf_map_update_elem()?
> +
> +
nit: extra new line
> + close(fd);
> + bpf_object__close(obj);
> + return;
> +
> +out_mapinmap:
> + close(fd);
> + exit(1);
> +}
> +
> #define MAP_SIZE (32 * 1024)
>
> static void test_map_large(void)
> @@ -1554,6 +1628,8 @@ static void run_all_tests(void)
>
> test_queuemap(0, NULL);
> test_stackmap(0, NULL);
> +
> + test_mapinmap();
> }
>
> int main(void)
> --
> 2.15.1
>
^ permalink raw reply
* Re: [PATCH iproute2-next 2/8] json: add %hhu helpers
From: Stephen Hemminger @ 2018-11-20 1:18 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: dsahern, oss-drivers, netdev
In-Reply-To: <20181119230335.11771-3-jakub.kicinski@netronome.com>
On Mon, 19 Nov 2018 15:03:29 -0800
Jakub Kicinski <jakub.kicinski@netronome.com> wrote:
> Add helpers for printing char-size values.
>
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
> ---
> include/json_print.h | 1 +
> include/json_writer.h | 2 ++
> lib/json_print.c | 1 +
> lib/json_writer.c | 11 +++++++++++
> 4 files changed, 15 insertions(+)
>
> diff --git a/include/json_print.h b/include/json_print.h
> index 218da31a73fe..25954070db27 100644
> --- a/include/json_print.h
> +++ b/include/json_print.h
> @@ -64,6 +64,7 @@ _PRINT_FUNC(null, const char*);
> _PRINT_FUNC(string, const char*);
> _PRINT_FUNC(uint, unsigned int);
> _PRINT_FUNC(u64, uint64_t);
> +_PRINT_FUNC(hhu, unsigned char);
> _PRINT_FUNC(hu, unsigned short);
> _PRINT_FUNC(hex, unsigned int);
> _PRINT_FUNC(0xhex, unsigned long long int);
> diff --git a/include/json_writer.h b/include/json_writer.h
> index 0c8831c1136d..354c27541e89 100644
> --- a/include/json_writer.h
> +++ b/include/json_writer.h
> @@ -38,6 +38,7 @@ void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
> void jsonw_uint(json_writer_t *self, unsigned int number);
> void jsonw_u64(json_writer_t *self, uint64_t number);
> void jsonw_xint(json_writer_t *self, uint64_t number);
> +void jsonw_hhu(json_writer_t *self, unsigned char num);
> void jsonw_hu(json_writer_t *self, unsigned short number);
> void jsonw_int(json_writer_t *self, int number);
> void jsonw_s64(json_writer_t *self, int64_t number);
> @@ -52,6 +53,7 @@ void jsonw_float_field(json_writer_t *self, const char *prop, double num);
> void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num);
> void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num);
> void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num);
> +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num);
> void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
> void jsonw_int_field(json_writer_t *self, const char *prop, int num);
> void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num);
> diff --git a/lib/json_print.c b/lib/json_print.c
> index f7ef41c1570f..4f5fef195fde 100644
> --- a/lib/json_print.c
> +++ b/lib/json_print.c
> @@ -118,6 +118,7 @@ void close_json_array(enum output_type type, const char *str)
> }
> _PRINT_FUNC(int, int);
> _PRINT_FUNC(s64, int64_t);
> +_PRINT_FUNC(hhu, unsigned char);
> _PRINT_FUNC(hu, unsigned short);
> _PRINT_FUNC(uint, unsigned int);
> _PRINT_FUNC(u64, uint64_t);
> diff --git a/lib/json_writer.c b/lib/json_writer.c
> index 68890b34ee92..46eff6ad9828 100644
> --- a/lib/json_writer.c
> +++ b/lib/json_writer.c
> @@ -211,6 +211,11 @@ void jsonw_float(json_writer_t *self, double num)
> jsonw_printf(self, "%g", num);
> }
>
> +void jsonw_hhu(json_writer_t *self, unsigned char num)
> +{
> + jsonw_printf(self, "%hhu", num);
> +}
> +
> void jsonw_hu(json_writer_t *self, unsigned short num)
> {
> jsonw_printf(self, "%hu", num);
> @@ -288,6 +293,12 @@ void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num)
> jsonw_xint(self, num);
> }
>
> +void jsonw_hhu_field(json_writer_t *self, const char *prop, unsigned char num)
> +{
> + jsonw_name(self, prop);
> + jsonw_hhu(self, num);
> +}
> +
> void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
> {
> jsonw_name(self, prop);
Do you really need this? it turns out that because of C type conversions print_uint should just
work?
^ permalink raw reply
* Re: [PATCH net-next v3 1/6] net: phy: mscc: migrate to phy_select/restore_page functions
From: Andreas Schwab @ 2018-11-20 11:39 UTC (permalink / raw)
To: Andrew Lunn
Cc: Alexandre Belloni, Quentin Schulz, davem, f.fainelli,
allan.nielsen, linux-kernel, netdev, thomas.petazzoni,
linux-riscv
In-Reply-To: <20181119161204.GC2393@lunn.ch>
On Nov 19 2018, Andrew Lunn <andrew@lunn.ch> wrote:
> I still don't see why that would cause a hang.
[ 996.370000] [<ffffffe0011d7324>] mutex_lock+0x22/0x2a
[ 996.380000] [<ffffffe001039ba0>] mdiobus_read+0x36/0x60
[ 996.380000] [<ffffffe00103b82c>] vsc85xx_config_init+0x4c/0x1e2
vsc85xx_config_init is calling mdiobus_read while holding the mdio_lock.
Andreas.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply
* Re: [PATCH bpf-next 1/2] bpf: adding support for map in map in libbpf
From: Y Song @ 2018-11-20 1:12 UTC (permalink / raw)
To: tehnerd; +Cc: Alexei Starovoitov, Daniel Borkmann, Jakub Kicinski, netdev
In-Reply-To: <20181120000626.16825-2-tehnerd@tehnerd.com>
On Mon, Nov 19, 2018 at 4:13 PM Nikita V. Shirokov <tehnerd@tehnerd.com> wrote:
>
> idea is pretty simple. for specified map (pointed by struct bpf_map)
> we would provide descriptor of already loaded map, which is going to be
> used as a prototype for inner map. proposed workflow:
> 1) open bpf's object (bpf_object__open)
> 2) create bpf's map which is going to be used as a prototype
> 3) find (by name) map-in-map which you want to load and update w/
> descriptor of inner map w/ a new helper from this patch
> 4) load bpf program w/ bpf_object__load
>
> inner_map_fd is ignored by any other maps asidef from (hash|array) of
> maps
>
> Signed-off-by: Nikita V. Shirokov <tehnerd@tehnerd.com>
> ---
> tools/lib/bpf/libbpf.c | 7 +++++++
> tools/lib/bpf/libbpf.h | 2 ++
> 2 files changed, 9 insertions(+)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index a01eb9584e52..a2ee1b1a93b6 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -163,6 +163,7 @@ struct bpf_map {
> char *name;
> size_t offset;
> int map_ifindex;
> + int inner_map_fd;
> struct bpf_map_def def;
> __u32 btf_key_type_id;
> __u32 btf_value_type_id;
> @@ -1146,6 +1147,7 @@ bpf_object__create_maps(struct bpf_object *obj)
> create_attr.btf_fd = 0;
> create_attr.btf_key_type_id = 0;
> create_attr.btf_value_type_id = 0;
> + create_attr.inner_map_fd = map->inner_map_fd;
>
> if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
> create_attr.btf_fd = btf__fd(obj->btf);
> @@ -2562,6 +2564,11 @@ void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
> map->map_ifindex = ifindex;
> }
>
> +void bpf_map__add_inner_map_fd(struct bpf_map *map, const int fd)
Do we need "const" attribute here?
> +{
> + map->inner_map_fd = fd;
> +}
> +
> static struct bpf_map *
> __bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
> {
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index b1686a787102..7cb00cd41789 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -293,6 +293,8 @@ LIBBPF_API void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
> LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
> LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
>
> +LIBBPF_API void bpf_map__add_inner_map_fd(struct bpf_map *map, const int fd);
> +
> LIBBPF_API long libbpf_get_error(const void *ptr);
>
> struct bpf_prog_load_attr {
> --
> 2.15.1
>
^ permalink raw reply
* Re: [PATCH bpf-next v2] filter: add BPF_ADJ_ROOM_DATA mode to bpf_skb_adjust_room()
From: Daniel Borkmann @ 2018-11-20 1:06 UTC (permalink / raw)
To: Nicolas Dichtel, kafai; +Cc: ast, davem, netdev
In-Reply-To: <20181113163517.4185-1-nicolas.dichtel@6wind.com>
On 11/13/2018 05:35 PM, Nicolas Dichtel wrote:
> This new mode enables to add or remove an l2 header in a programmatic way
> with cls_bpf.
> For example, it enables to play with mpls headers.
>
> Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
> Acked-by: Martin KaFai Lau <kafai@fb.com>
(Sorry for late reply, swamped due to Plumbers.)
> ---
>
> v2: use skb_set_network_header()
>
> include/uapi/linux/bpf.h | 3 ++
> net/core/filter.c | 53 ++++++++++++++++++++++++++++++++++
> tools/include/uapi/linux/bpf.h | 3 ++
> 3 files changed, 59 insertions(+)
>
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 47d606d744cc..9762ef3a536c 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -1467,6 +1467,8 @@ union bpf_attr {
> *
> * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer
> * (room space is added or removed below the layer 3 header).
> + * * **BPF_ADJ_ROOM_DATA**: Adjust room at the beginning of the
> + * packet (room space is added or removed below skb->data).
Nit: in this case it would probably make more sense to name it BPF_ADJ_ROOM_MAC.
BPF_ADJ_ROOM_DATA reads like we're adjusting payload data.
> * All values for *flags* are reserved for future usage, and must
> * be left at zero.
> @@ -2412,6 +2414,7 @@ enum bpf_func_id {
> /* Mode for BPF_FUNC_skb_adjust_room helper. */
> enum bpf_adj_room_mode {
> BPF_ADJ_ROOM_NET,
> + BPF_ADJ_ROOM_DATA,
> };
>
> /* Mode for BPF_FUNC_skb_load_bytes_relative helper. */
> diff --git a/net/core/filter.c b/net/core/filter.c
> index 53d50fb75ea1..e56da3077dca 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -2884,6 +2884,57 @@ static int bpf_skb_adjust_net(struct sk_buff *skb, s32 len_diff)
> return ret;
> }
>
> +static int bpf_skb_data_shrink(struct sk_buff *skb, u32 len)
> +{
> + unsigned short hhlen = skb->dev->header_ops ?
> + skb->dev->hard_header_len : 0;
> + int ret;
> +
> + ret = skb_unclone(skb, GFP_ATOMIC);
> + if (unlikely(ret < 0))
> + return ret;
> +
> + __skb_pull(skb, len);
> + skb_reset_mac_header(skb);
> + skb_set_network_header(skb, hhlen);
> + skb_reset_transport_header(skb);
> + return 0;
> +}
> +
> +static int bpf_skb_data_grow(struct sk_buff *skb, u32 len)
> +{
> + unsigned short hhlen = skb->dev->header_ops ?
> + skb->dev->hard_header_len : 0;
> + int ret;
> +
> + ret = skb_cow(skb, len);
> + if (unlikely(ret < 0))
> + return ret;
> +
> + skb_push(skb, len);
> + skb_reset_mac_header(skb);
Looks like this could leak uninitialized mem into the BPF prog as it's
not zeroed out. Can't we just reuse bpf_skb_generic_push() and the
bpf_skb_generic_pop()?
bpf_skb_vlan_push() and bpf_skb_vlan_pop() do a bpf_push_mac_rcsum() /
bpf_pull_mac_rcsum() in order to not screw up things like csum complete.
Wouldn't this be needed here as well?
How does this interact with MPLS GSO?
If the packet gets pushed back into the stack, would AF_MPLS handle it?
Probably good to add test cases to BPF kselftest suite with it.
Don't we need to reject the packet in case of skb->encapsulation?
Looking at push_mpls() and pop_mpls() from OVS implementation, do we
also need to deal with skb inner network header / proto?
Given all this would it rather make sense to add MPLS specific helpers
in similar fashion to the vlan ones we have?
Is there any other use case aside from MPLS?
> + return 0;
> +}
> +
> +static int bpf_skb_adjust_data(struct sk_buff *skb, s32 len_diff)
> +{
> + u32 len_diff_abs = abs(len_diff);
> + bool shrink = len_diff < 0;
> + int ret;
> +
> + if (unlikely(len_diff_abs > 0xfffU))
> + return -EFAULT;
> +
> + if (shrink && len_diff_abs >= skb_headlen(skb))
> + return -EFAULT;
> +
> + ret = shrink ? bpf_skb_data_shrink(skb, len_diff_abs) :
> + bpf_skb_data_grow(skb, len_diff_abs);
Hmm, I think this has a bug in that the above two do not adjust
skb->mac_len. Then things like cls_bpf_classify() will __skb_pull()
based on the old mac_len, getting you to a wrong offset in the
packet when this is for example pushed back into ingress, etc.
> + bpf_compute_data_pointers(skb);
> + return ret;
> +}
> +
> BPF_CALL_4(bpf_skb_adjust_room, struct sk_buff *, skb, s32, len_diff,
> u32, mode, u64, flags)
> {
> @@ -2891,6 +2942,8 @@ BPF_CALL_4(bpf_skb_adjust_room, struct sk_buff *, skb, s32, len_diff,
> return -EINVAL;
> if (likely(mode == BPF_ADJ_ROOM_NET))
> return bpf_skb_adjust_net(skb, len_diff);
> + if (likely(mode == BPF_ADJ_ROOM_DATA))
> + return bpf_skb_adjust_data(skb, len_diff);
>
> return -ENOTSUPP;
> }
> diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> index 852dc17ab47a..47407fd5162b 100644
> --- a/tools/include/uapi/linux/bpf.h
> +++ b/tools/include/uapi/linux/bpf.h
> @@ -1467,6 +1467,8 @@ union bpf_attr {
> *
> * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer
> * (room space is added or removed below the layer 3 header).
> + * * **BPF_ADJ_ROOM_DATA**: Adjust room at the beginning of the
> + * packet (room space is added or removed below skb->data).
> *
> * All values for *flags* are reserved for future usage, and must
> * be left at zero.
> @@ -2408,6 +2410,7 @@ enum bpf_func_id {
> /* Mode for BPF_FUNC_skb_adjust_room helper. */
> enum bpf_adj_room_mode {
> BPF_ADJ_ROOM_NET,
> + BPF_ADJ_ROOM_DATA,
> };
>
> /* Mode for BPF_FUNC_skb_load_bytes_relative helper. */
>
^ permalink raw reply
* Re: 9p/RDMA for syzkaller (Was: BUG: corrupted list in p9_read_work)
From: Dominique Martinet @ 2018-11-20 11:28 UTC (permalink / raw)
To: Dmitry Vyukov
Cc: Leon Romanovsky, David Miller, Eric Van Hensbergen, LKML,
Latchesar Ionkov, netdev, Ron Minnich, syzkaller-bugs,
v9fs-developer
In-Reply-To: <CACT4Y+YzEUouqi7daSBcJWak67rEdMEichWHpeZH-ggO+fD_nw@mail.gmail.com>
Dmitry Vyukov wrote on Tue, Nov 20, 2018:
> I don't understand most of the words here, so I probably won't be of help.
>
> Leon told me that there is some kind of software emulation RDMA
> interface (CONFIG_RDMA_RXE?). Maybe it can be used to create an
> emulated RDMA interface per test process and use it to communicate
> with 9p client?
Yes, rxe is what you made me look into and is what I was talking about
here - the problem is I can't seem to figure how to use it *as udp*
within a single machine.
With two VMs or a remote outside of the VM when I try to open a RDMA
connection with that software emulation I get nicely understandable udp
packets ; but within a single VM there is nothing visible at the udp
layer, even if I explicitely configure multiple interfaces and try to
use one as rxe and another without it that would act as "outside" -- the
kernel doesn't send anything to the normal interface like it normally
would.
There might be other ways around it (I'm starting to think very ugly
things like really pretend to connect to an IP outside of the VM, but
capture the outgoing traffic with pcap and forge the replies), but I'd
rather not have to go to this extent...
--
Dominique
^ permalink raw reply
* Re: 9p/RDMA for syzkaller (Was: BUG: corrupted list in p9_read_work)
From: Dmitry Vyukov @ 2018-11-20 11:20 UTC (permalink / raw)
To: Dominique Martinet
Cc: Leon Romanovsky, David Miller, Eric Van Hensbergen, LKML,
Latchesar Ionkov, netdev, Ron Minnich, syzkaller-bugs,
v9fs-developer
In-Reply-To: <20181117084649.GA24182@nautica>
On Sat, Nov 17, 2018 at 9:46 AM, Dominique Martinet
<asmadeus@codewreck.org> wrote:
> Dominique Martinet wrote on Fri, Oct 12, 2018:
>> Hmm, good point; I hadn't looked at the network level how this is
>> emulated.
>> When I use a single VM I do not see anything with tcpdump on any
>> interface, so I assume the kernel short-cuts the interface in this case.
>> When communicating between two machines there obviously is traffic; it
>> appears to be transported over udp - I see the messages I sent in plain
>> text in the dump and there is only a handful of packets for the whole
>> connecting and teardown so it's definitely much simpler.
>>
>> This might have some knob I am not aware of to force the driver to send
>> udp in the local setup, if we can it's going to be much easier to
>> reimplement the rxe emulation protocol with raw syscalls than what I was
>> describing earlier...
>
> I've taken a little bit of time to look at this.
> Unfortunately, I haven't been able to find any knob to make a
> single-machine setup send UDP over an interface; would have hoped that
> playing with multiple interfaces might be a way around this (e.g. use
> the rxe driver on one interface and listen on the udp port on the other
> one), but I couldn't get this to work and don't see anything in the code
> that hints it might be configurable.
> It seems to realize that the remote IP is assigned locally anyway and
> while I cannot "bind" to an address that wasn't added to the rxe driver
> at the rdma_cm level, trying to connect to it never sends any packet to
> a ncat listening on 4791/udp on that interface...
> If anyone has an idea, it'd be great!
>
> Protocole-wise, once we get that to work it doesn't look very difficult
> to implement some basic "accept connection and send acks/replies" small
> server, but we need the first part to work...
I don't understand most of the words here, so I probably won't be of help.
Leon told me that there is some kind of software emulation RDMA
interface (CONFIG_RDMA_RXE?). Maybe it can be used to create an
emulated RDMA interface per test process and use it to communicate
with 9p client?
^ permalink raw reply
* [PATCH bpf-next] bpf: libbpf: retry program creation without the name
From: Stanislav Fomichev @ 2018-11-20 0:46 UTC (permalink / raw)
To: netdev, daniel; +Cc: ast, vladum, Stanislav Fomichev
[Recent commit 23499442c319 ("bpf: libbpf: retry map creation without
the name") fixed this issue for maps, let's do the same for programs.]
Since commit 88cda1c9da02 ("bpf: libbpf: Provide basic API support
to specify BPF obj name"), libbpf unconditionally sets bpf_attr->name
for programs. Pre v4.14 kernels don't know about programs names and
return an error about unexpected non-zero data. Retry sys_bpf without
a program name to cover older kernels.
Signed-off-by: Stanislav Fomichev <sdf@google.com>
---
tools/lib/bpf/bpf.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 961e1b9fc592..cbe9d757c646 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -212,6 +212,16 @@ int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
if (fd >= 0 || !log_buf || !log_buf_sz)
return fd;
+ if (fd < 0 && errno == E2BIG && load_attr->name) {
+ /* Retry the same syscall, but without the name.
+ * Pre v4.14 kernels don't support prog names.
+ */
+ memset(attr.prog_name, 0, sizeof(attr.prog_name));
+ fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (fd >= 0 || !log_buf || !log_buf_sz)
+ return fd;
+ }
+
/* Try again with log */
attr.log_buf = ptr_to_u64(log_buf);
attr.log_size = log_buf_sz;
--
2.19.1.1215.g8438c0b245-goog
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox