All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next] samples/bpf: add xdp_redirect_map with xdp_prog support
@ 2020-11-10 12:46 Hangbin Liu
  2020-11-10 14:25 ` Jesper Dangaard Brouer
  2020-11-26  8:43 ` [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map Hangbin Liu
  0 siblings, 2 replies; 33+ messages in thread
From: Hangbin Liu @ 2020-11-10 12:46 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	David S . Miller, Toke Høiland-Jørgensen, Tariq Toukan,
	Hangbin Liu

This patch add running xdp program on egress interface support for
xdp_redirect_map sample. The new prog will change the IP ttl based
on egress ifindex.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
 samples/bpf/xdp_redirect_map_kern.c | 74 ++++++++++++++++++++++++++++-
 samples/bpf/xdp_redirect_map_user.c | 21 ++++----
 2 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..e5ebff2271f4 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -22,7 +22,7 @@
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
-	__uint(value_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
 	__uint(max_entries, 100);
 } tx_port SEC(".maps");
 
@@ -52,6 +52,48 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
+static __always_inline __u16 csum_fold_helper(__u32 csum)
+{
+	__u32 sum;
+	sum = (csum & 0xffff) + (csum >> 16);
+	sum += (sum >> 16);
+	return ~sum;
+}
+
+static __always_inline __u16 ipv4_csum(__u16 seed, struct iphdr *iphdr_new,
+				       struct iphdr *iphdr_old)
+{
+	__u32 csum, size = sizeof(struct iphdr);
+	csum = bpf_csum_diff((__be32 *)iphdr_old, size,
+			     (__be32 *)iphdr_new, size, seed);
+	return csum_fold_helper(csum);
+}
+
+static void parse_ipv4(void *data, u64 nh_off, void *data_end, u8 ttl)
+{
+	struct iphdr *iph = data + nh_off;
+	struct iphdr iph_old;
+	__u16 csum_old;
+
+	if (iph + 1 > data_end)
+		return;
+
+	iph_old = *iph;
+	csum_old = iph->check;
+	iph->ttl = ttl;
+	iph->check = ipv4_csum(~csum_old, iph, &iph_old);
+}
+
+static void parse_ipv6(void *data, u64 nh_off, void *data_end, u8 hop_limit)
+{
+	struct ipv6hdr *ip6h = data + nh_off;
+
+	if (ip6h + 1 > data_end)
+		return;
+
+	ip6h->hop_limit = hop_limit;
+}
+
 SEC("xdp_redirect_map")
 int xdp_redirect_map_prog(struct xdp_md *ctx)
 {
@@ -82,6 +124,36 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	return bpf_redirect_map(&tx_port, vport, 0);
 }
 
+/* This map prog will set new IP ttl based on egress ifindex */
+SEC("xdp_devmap/map_prog")
+int xdp_devmap_prog(struct xdp_md *ctx)
+{
+	char fmt[] = "devmap redirect: egress dev %u with new ttl %u\n";
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	u16 h_proto;
+	u64 nh_off;
+	u8 ttl;
+
+	nh_off = sizeof(struct ethhdr);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	/* set new ttl based on egress ifindex */
+	ttl = ctx->egress_ifindex % 64;
+
+	h_proto = eth->h_proto;
+	if (h_proto == htons(ETH_P_IP))
+		parse_ipv4(data, nh_off, data_end, ttl);
+	else if (h_proto == htons(ETH_P_IPV6))
+		parse_ipv6(data, nh_off, data_end, ttl);
+
+	bpf_trace_printk(fmt, sizeof(fmt), ctx->egress_ifindex, ttl);
+
+	return XDP_PASS;
+}
+
 /* Redirect require an XDP bpf_prog loaded on the TX device */
 SEC("xdp_redirect_dummy")
 int xdp_redirect_dummy_prog(struct xdp_md *ctx)
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 35e16dee613e..9a95eab629bd 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -98,12 +98,13 @@ int main(int argc, char **argv)
 {
 	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int prog_fd, dummy_prog_fd, devmap_prog_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
 	const char *optstr = "FSN";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
@@ -157,16 +158,18 @@ int main(int argc, char **argv)
 		return 1;
 
 	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
+	devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog");
+	dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
+	if (!prog || !devmap_prog || !dummy_prog) {
 		printf("finding a prog in obj file failed\n");
 		return 1;
 	}
 	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
+	 * so we're missing the fd for devmap and dummy prog
 	 */
+	devmap_prog_fd = bpf_program__fd(devmap_prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || devmap_prog_fd < 0 || dummy_prog_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
@@ -209,7 +212,9 @@ int main(int argc, char **argv)
 	signal(SIGTERM, int_exit);
 
 	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	devmap_val.bpf_prog.fd = devmap_prog_fd;
+	devmap_val.ifindex = ifindex_out;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.25.4


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH bpf-next] samples/bpf: add xdp_redirect_map with xdp_prog support
  2020-11-10 12:46 [PATCH bpf-next] samples/bpf: add xdp_redirect_map with xdp_prog support Hangbin Liu
@ 2020-11-10 14:25 ` Jesper Dangaard Brouer
  2020-11-10 15:24   ` Maciej Fijalkowski
  2020-11-11  1:12   ` Hangbin Liu
  2020-11-26  8:43 ` [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map Hangbin Liu
  1 sibling, 2 replies; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2020-11-10 14:25 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan, brouer,
	Jakub Kicinski

On Tue, 10 Nov 2020 20:46:39 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> This patch add running xdp program on egress interface support for
> xdp_redirect_map sample. The new prog will change the IP ttl based
> on egress ifindex.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> ---
>  samples/bpf/xdp_redirect_map_kern.c | 74 ++++++++++++++++++++++++++++-
>  samples/bpf/xdp_redirect_map_user.c | 21 ++++----

Hmmm... I don't think is it a good idea to modify xdp_redirect_map this way.

The xdp_redirect_map is used for comparative benchmarking and
mentioned+used in scientific articles.  As far as I can see, this
change will default slowdown xdp_redirect_map performance, right?

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH bpf-next] samples/bpf: add xdp_redirect_map with xdp_prog support
  2020-11-10 14:25 ` Jesper Dangaard Brouer
@ 2020-11-10 15:24   ` Maciej Fijalkowski
  2020-11-11  1:12   ` Hangbin Liu
  1 sibling, 0 replies; 33+ messages in thread
From: Maciej Fijalkowski @ 2020-11-10 15:24 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: Hangbin Liu, bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan, Jakub Kicinski

On Tue, Nov 10, 2020 at 03:25:10PM +0100, Jesper Dangaard Brouer wrote:
> On Tue, 10 Nov 2020 20:46:39 +0800
> Hangbin Liu <liuhangbin@gmail.com> wrote:
> 
> > This patch add running xdp program on egress interface support for
> > xdp_redirect_map sample. The new prog will change the IP ttl based
> > on egress ifindex.
> > 
> > Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> > ---
> >  samples/bpf/xdp_redirect_map_kern.c | 74 ++++++++++++++++++++++++++++-
> >  samples/bpf/xdp_redirect_map_user.c | 21 ++++----
> 
> Hmmm... I don't think is it a good idea to modify xdp_redirect_map this way.
> 
> The xdp_redirect_map is used for comparative benchmarking and
> mentioned+used in scientific articles.  As far as I can see, this
> change will default slowdown xdp_redirect_map performance, right?

+1

User should be able to trigger attachment of this xdp egress prog by
himself. I don't like having it as a mandatory thing on this sample.

> 
> -- 
> Best regards,
>   Jesper Dangaard Brouer
>   MSc.CS, Principal Kernel Engineer at Red Hat
>   LinkedIn: http://www.linkedin.com/in/brouer
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH bpf-next] samples/bpf: add xdp_redirect_map with xdp_prog support
  2020-11-10 14:25 ` Jesper Dangaard Brouer
  2020-11-10 15:24   ` Maciej Fijalkowski
@ 2020-11-11  1:12   ` Hangbin Liu
  1 sibling, 0 replies; 33+ messages in thread
From: Hangbin Liu @ 2020-11-11  1:12 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan, Jakub Kicinski

On Tue, Nov 10, 2020 at 03:25:10PM +0100, Jesper Dangaard Brouer wrote:
> On Tue, 10 Nov 2020 20:46:39 +0800
> Hangbin Liu <liuhangbin@gmail.com> wrote:
> 
> > This patch add running xdp program on egress interface support for
> > xdp_redirect_map sample. The new prog will change the IP ttl based
> > on egress ifindex.
> > 
> > Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> > ---
> >  samples/bpf/xdp_redirect_map_kern.c | 74 ++++++++++++++++++++++++++++-
> >  samples/bpf/xdp_redirect_map_user.c | 21 ++++----
> 
> Hmmm... I don't think is it a good idea to modify xdp_redirect_map this way.
> 
> The xdp_redirect_map is used for comparative benchmarking and

Hi Jesper,

Would you please help explain or give some example about how to do
comparative benchmarking with xdp_redirect_map, just count the TX pkts?

Thanks
Hangbin
> mentioned+used in scientific articles.  As far as I can see, this
> change will default slowdown xdp_redirect_map performance, right?
> 
> -- 
> Best regards,
>   Jesper Dangaard Brouer
>   MSc.CS, Principal Kernel Engineer at Red Hat
>   LinkedIn: http://www.linkedin.com/in/brouer
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-10 12:46 [PATCH bpf-next] samples/bpf: add xdp_redirect_map with xdp_prog support Hangbin Liu
  2020-11-10 14:25 ` Jesper Dangaard Brouer
@ 2020-11-26  8:43 ` Hangbin Liu
  2020-11-26 10:51   ` Jesper Dangaard Brouer
                     ` (2 more replies)
  1 sibling, 3 replies; 33+ messages in thread
From: Hangbin Liu @ 2020-11-26  8:43 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski, Hangbin Liu

Current sample test xdp_redirect_map only count pkts on ingress. But we
can't know whether the pkts are redirected or dropped. So add a counter
on egress interface so we could know how many pkts are redirect in fact.

sample result:

$ ./xdp_redirect_map -X veth1 veth2
input: 5 output: 6
libbpf: elf: skipping unrecognized data section(9) .rodata.str1.16
libbpf: elf: skipping unrecognized data section(23) .eh_frame
libbpf: elf: skipping relo section(24) .rel.eh_frame for section(23) .eh_frame
in ifindex 5:          1 pkt/s, out ifindex 6:          1 pkt/s
in ifindex 5:          1 pkt/s, out ifindex 6:          1 pkt/s
in ifindex 5:          0 pkt/s, out ifindex 6:          0 pkt/s
in ifindex 5:         68 pkt/s, out ifindex 6:         68 pkt/s
in ifindex 5:         91 pkt/s, out ifindex 6:         91 pkt/s
in ifindex 5:         91 pkt/s, out ifindex 6:         91 pkt/s
in ifindex 5:         66 pkt/s, out ifindex 6:         66 pkt/s

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X

---
 samples/bpf/xdp_redirect_map_kern.c |  26 +++--
 samples/bpf/xdp_redirect_map_user.c | 142 ++++++++++++++++++----------
 2 files changed, 113 insertions(+), 55 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..fd6704a4f7e2 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -22,19 +22,19 @@
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
-	__uint(value_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
 	__uint(max_entries, 100);
 } tx_port SEC(".maps");
 
-/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
- * feedback.  Redirect TX errors can be caught via a tracepoint.
+/* Count RX/TX packets, use key 0 for rx pkt count, key 1 for tx
+ * pkt count.
  */
 struct {
 	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
 	__type(key, u32);
 	__type(value, long);
-	__uint(max_entries, 1);
-} rxcnt SEC(".maps");
+	__uint(max_entries, 2);
+} pktcnt SEC(".maps");
 
 static void swap_src_dst_mac(void *data)
 {
@@ -72,7 +72,7 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	vport = 0;
 
 	/* count packet in global counter */
-	value = bpf_map_lookup_elem(&rxcnt, &key);
+	value = bpf_map_lookup_elem(&pktcnt, &key);
 	if (value)
 		*value += 1;
 
@@ -82,6 +82,20 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	return bpf_redirect_map(&tx_port, vport, 0);
 }
 
+SEC("xdp_devmap/map_prog")
+int xdp_devmap_prog(struct xdp_md *ctx)
+{
+	long *value;
+	u32 key = 1;
+
+	/* count packet in global counter */
+	value = bpf_map_lookup_elem(&pktcnt, &key);
+	if (value)
+		*value += 1;
+
+	return XDP_PASS;
+}
+
 /* Redirect require an XDP bpf_prog loaded on the TX device */
 SEC("xdp_redirect_dummy")
 int xdp_redirect_dummy_prog(struct xdp_md *ctx)
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 35e16dee613e..8bdec0865e1d 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -21,12 +21,13 @@
 
 static int ifindex_in;
 static int ifindex_out;
-static bool ifindex_out_xdp_dummy_attached = true;
+static bool ifindex_out_xdp_dummy_attached = false;
+static bool xdp_prog_attached = false;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
-static int rxcnt_map_fd;
+static int pktcnt_map_fd;
 
 static void int_exit(int sig)
 {
@@ -60,26 +61,46 @@ static void int_exit(int sig)
 	exit(0);
 }
 
-static void poll_stats(int interval, int ifindex)
+static void poll_stats(int interval, int if_ingress, int if_egress)
 {
 	unsigned int nr_cpus = bpf_num_possible_cpus();
-	__u64 values[nr_cpus], prev[nr_cpus];
+	__u64 values[nr_cpus], in_prev[nr_cpus], e_prev[nr_cpus];
+	__u64 sum;
+	__u32 key;
+	int i;
 
-	memset(prev, 0, sizeof(prev));
+	memset(in_prev, 0, sizeof(in_prev));
+	memset(e_prev, 0, sizeof(e_prev));
 
 	while (1) {
-		__u64 sum = 0;
-		__u32 key = 0;
-		int i;
+		sum = 0;
+		key = 0;
 
 		sleep(interval);
-		assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
-		for (i = 0; i < nr_cpus; i++)
-			sum += (values[i] - prev[i]);
-		if (sum)
-			printf("ifindex %i: %10llu pkt/s\n",
-			       ifindex, sum / interval);
-		memcpy(prev, values, sizeof(values));
+		if (bpf_map_lookup_elem(pktcnt_map_fd, &key, values) == 0) {
+			for (i = 0; i < nr_cpus; i++)
+				sum += (values[i] - in_prev[i]);
+			if (sum)
+				printf("in ifindex %i: %10llu pkt/s",
+				       if_ingress, sum / interval);
+			memcpy(in_prev, values, sizeof(values));
+		}
+
+		if (!xdp_prog_attached) {
+			printf("\n");
+			continue;
+		}
+
+		sum = 0;
+		key = 1;
+		if (bpf_map_lookup_elem(pktcnt_map_fd, &key, values) == 0) {
+			for (i = 0; i < nr_cpus; i++)
+				sum += (values[i] - e_prev[i]);
+			if (sum)
+				printf(", out ifindex %i: %10llu pkt/s\n",
+				       if_egress, sum / interval);
+			memcpy(e_prev, values, sizeof(values));
+		}
 	}
 }
 
@@ -90,7 +111,8 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
@@ -98,13 +120,14 @@ int main(int argc, char **argv)
 {
 	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int prog_fd, dummy_prog_fd, devmap_prog_fd = -1;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
@@ -121,6 +144,9 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_prog_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
@@ -157,23 +183,14 @@ int main(int argc, char **argv)
 		return 1;
 
 	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
-	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
-	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
-		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
+	if (!prog || prog_fd <0) {
+		printf("finding prog in obj file failed\n");
 		return 1;
 	}
 
 	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
-	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	pktcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "pktcnt");
+	if (tx_port_map_fd < 0 || pktcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -190,32 +207,59 @@ int main(int argc, char **argv)
 	}
 	prog_id = info.id;
 
-	/* Loading dummy XDP prog on out-device */
-	if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
-			    (xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) < 0) {
-		printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
-		ifindex_out_xdp_dummy_attached = false;
-	}
+	/* Loading dummy XDP prog on out-device if no xdp_prog_attached*/
+	if (xdp_prog_attached) {
+		devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			return 1;
+		}
+		devmap_prog_fd = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd < 0) {
+			printf("find devmap_prog fd: %s\n", strerror(errno));
+			return 1;
+		}
+	} else {
+		dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
+		if (!dummy_prog) {
+			printf("finding dummy_prog in obj file failed\n");
+			return 1;
+		}
 
-	memset(&info, 0, sizeof(info));
-	ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
-	if (ret) {
-		printf("can't get prog info - %s\n", strerror(errno));
-		return ret;
+		dummy_prog_fd = bpf_program__fd(dummy_prog);
+		if (dummy_prog_fd < 0) {
+			printf("find dummy_prog fd: %s\n", strerror(errno));
+			return 1;
+		}
+
+		if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
+				    (xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) == 0) {
+			ifindex_out_xdp_dummy_attached = true;
+		} else {
+			printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
+		}
+
+		memset(&info, 0, sizeof(info));
+		ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
+		if (ret) {
+			printf("can't get prog info - %s\n", strerror(errno));
+			return ret;
+		}
+		dummy_prog_id = info.id;
 	}
-	dummy_prog_id = info.id;
 
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
-		perror("bpf_update_elem");
+		perror("bpf_update_elem tx_port_map_fd");
 		goto out;
 	}
 
-	poll_stats(2, ifindex_out);
+	poll_stats(2, ifindex_in, ifindex_out);
 
 out:
 	return 0;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-26  8:43 ` [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map Hangbin Liu
@ 2020-11-26 10:51   ` Jesper Dangaard Brouer
  2020-11-26 14:19     ` Hangbin Liu
  2020-11-27  6:31   ` Yonghong Song
  2020-12-08  8:18   ` [PATCHv3 " Hangbin Liu
  2 siblings, 1 reply; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2020-11-26 10:51 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski, brouer

On Thu, 26 Nov 2020 16:43:25 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> Current sample test xdp_redirect_map only count pkts on ingress. But we
> can't know whether the pkts are redirected or dropped. So add a counter
> on egress interface so we could know how many pkts are redirect in fact.

This is not true.

The 2nd devmap XDP-prog will run in the same RX-context, so it doesn't
tell us if the redirect was successful.  I looked up the code, and the
2nd XDP-prog is even allowed to run when the egress driver doesn't
support the NDO to xmit (dev->netdev_ops->ndo_xdp_xmit), which is very
misleading, if you place a output counter here.

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


static inline int __xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp,
			       struct net_device *dev_rx)
{
	struct xdp_frame *xdpf;
	int err;

	if (!dev->netdev_ops->ndo_xdp_xmit)
		return -EOPNOTSUPP;

	err = xdp_ok_fwd_dev(dev, xdp->data_end - xdp->data);
	if (unlikely(err))
		return err;

	xdpf = xdp_convert_buff_to_frame(xdp);
	if (unlikely(!xdpf))
		return -EOVERFLOW;

	bq_enqueue(dev, xdpf, dev_rx);
	return 0;
}

static struct xdp_buff *dev_map_run_prog(struct net_device *dev,
					 struct xdp_buff *xdp,
					 struct bpf_prog *xdp_prog)
{
	struct xdp_txq_info txq = { .dev = dev };
	u32 act;

	xdp_set_data_meta_invalid(xdp);
	xdp->txq = &txq;

	act = bpf_prog_run_xdp(xdp_prog, xdp);
	switch (act) {
	case XDP_PASS:
		return xdp;
	case XDP_DROP:
		break;
	default:
		bpf_warn_invalid_xdp_action(act);
		fallthrough;
	case XDP_ABORTED:
		trace_xdp_exception(dev, xdp_prog, act);
		break;
	}

	xdp_return_buff(xdp);
	return NULL;
}

int dev_xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp,
		    struct net_device *dev_rx)
{
	return __xdp_enqueue(dev, xdp, dev_rx);
}

int dev_map_enqueue(struct bpf_dtab_netdev *dst, struct xdp_buff *xdp,
		    struct net_device *dev_rx)
{
	struct net_device *dev = dst->dev;

	if (dst->xdp_prog) {
		xdp = dev_map_run_prog(dev, xdp, dst->xdp_prog);
		if (!xdp)
			return 0;
	}
	return __xdp_enqueue(dev, xdp, dev_rx);
}


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-26 10:51   ` Jesper Dangaard Brouer
@ 2020-11-26 14:19     ` Hangbin Liu
  0 siblings, 0 replies; 33+ messages in thread
From: Hangbin Liu @ 2020-11-26 14:19 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski

On Thu, Nov 26, 2020 at 11:51:19AM +0100, Jesper Dangaard Brouer wrote:
> On Thu, 26 Nov 2020 16:43:25 +0800
> Hangbin Liu <liuhangbin@gmail.com> wrote:
> 
> > Current sample test xdp_redirect_map only count pkts on ingress. But we
> > can't know whether the pkts are redirected or dropped. So add a counter
> > on egress interface so we could know how many pkts are redirect in fact.
> 
> This is not true.
> 
> The 2nd devmap XDP-prog will run in the same RX-context, so it doesn't
> tell us if the redirect was successful.  I looked up the code, and the
> 2nd XDP-prog is even allowed to run when the egress driver doesn't
> support the NDO to xmit (dev->netdev_ops->ndo_xdp_xmit), which is very
> misleading, if you place a output counter here.
> 
> -- 
> Best regards,
>   Jesper Dangaard Brouer
>   MSc.CS, Principal Kernel Engineer at Red Hat
>   LinkedIn: http://www.linkedin.com/in/brouer
> 
> 
> static inline int __xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp,
> 			       struct net_device *dev_rx)
> {
> 	struct xdp_frame *xdpf;
> 	int err;
> 
> 	if (!dev->netdev_ops->ndo_xdp_xmit)
> 		return -EOPNOTSUPP;
> 

Err... You are right. I didn't read this part carefully.

Based on Toke's suggestion, maybe edit the src mac address to the egress
iface's mac address is a better example. WDYT?

And does it make any sense to run perf test for 2nd xdp program
for xdp_redirect_map?

Thanks
Hangbin


> 	err = xdp_ok_fwd_dev(dev, xdp->data_end - xdp->data);
> 	if (unlikely(err))
> 		return err;
> 
> 	xdpf = xdp_convert_buff_to_frame(xdp);
> 	if (unlikely(!xdpf))
> 		return -EOVERFLOW;
> 
> 	bq_enqueue(dev, xdpf, dev_rx);
> 	return 0;
> }
> 
> 
> int dev_map_enqueue(struct bpf_dtab_netdev *dst, struct xdp_buff *xdp,
> 		    struct net_device *dev_rx)
> {
> 	struct net_device *dev = dst->dev;
> 
> 	if (dst->xdp_prog) {
> 		xdp = dev_map_run_prog(dev, xdp, dst->xdp_prog);
> 		if (!xdp)
> 			return 0;
> 	}
> 	return __xdp_enqueue(dev, xdp, dev_rx);
> }
> 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-26  8:43 ` [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map Hangbin Liu
  2020-11-26 10:51   ` Jesper Dangaard Brouer
@ 2020-11-27  6:31   ` Yonghong Song
  2020-11-30  7:51     ` Hangbin Liu
  2020-12-08  8:18   ` [PATCHv3 " Hangbin Liu
  2 siblings, 1 reply; 33+ messages in thread
From: Yonghong Song @ 2020-11-27  6:31 UTC (permalink / raw)
  To: Hangbin Liu, bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski



On 11/26/20 12:43 AM, Hangbin Liu wrote:
> Current sample test xdp_redirect_map only count pkts on ingress. But we
> can't know whether the pkts are redirected or dropped. So add a counter
> on egress interface so we could know how many pkts are redirect in fact.
> 
> sample result:
> 
> $ ./xdp_redirect_map -X veth1 veth2
> input: 5 output: 6
> libbpf: elf: skipping unrecognized data section(9) .rodata.str1.16
> libbpf: elf: skipping unrecognized data section(23) .eh_frame
> libbpf: elf: skipping relo section(24) .rel.eh_frame for section(23) .eh_frame
> in ifindex 5:          1 pkt/s, out ifindex 6:          1 pkt/s
> in ifindex 5:          1 pkt/s, out ifindex 6:          1 pkt/s
> in ifindex 5:          0 pkt/s, out ifindex 6:          0 pkt/s
> in ifindex 5:         68 pkt/s, out ifindex 6:         68 pkt/s
> in ifindex 5:         91 pkt/s, out ifindex 6:         91 pkt/s
> in ifindex 5:         91 pkt/s, out ifindex 6:         91 pkt/s
> in ifindex 5:         66 pkt/s, out ifindex 6:         66 pkt/s
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> ---
> v2:
> a) use pkt counter instead of IP ttl modification on egress program
> b) make the egress program selectable by option -X
> 
> ---
>   samples/bpf/xdp_redirect_map_kern.c |  26 +++--
>   samples/bpf/xdp_redirect_map_user.c | 142 ++++++++++++++++++----------
>   2 files changed, 113 insertions(+), 55 deletions(-)
> 
> diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
> index 6489352ab7a4..fd6704a4f7e2 100644
> --- a/samples/bpf/xdp_redirect_map_kern.c
> +++ b/samples/bpf/xdp_redirect_map_kern.c
> @@ -22,19 +22,19 @@
>   struct {
>   	__uint(type, BPF_MAP_TYPE_DEVMAP);
>   	__uint(key_size, sizeof(int));
> -	__uint(value_size, sizeof(int));
> +	__uint(value_size, sizeof(struct bpf_devmap_val));
>   	__uint(max_entries, 100);
>   } tx_port SEC(".maps");
>   
> -/* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
> - * feedback.  Redirect TX errors can be caught via a tracepoint.
> +/* Count RX/TX packets, use key 0 for rx pkt count, key 1 for tx
> + * pkt count.
>    */
>   struct {
>   	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
>   	__type(key, u32);
>   	__type(value, long);
> -	__uint(max_entries, 1);
> -} rxcnt SEC(".maps");
> +	__uint(max_entries, 2);
> +} pktcnt SEC(".maps");
>   
>   static void swap_src_dst_mac(void *data)
>   {
> @@ -72,7 +72,7 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
>   	vport = 0;
>   
>   	/* count packet in global counter */
> -	value = bpf_map_lookup_elem(&rxcnt, &key);
> +	value = bpf_map_lookup_elem(&pktcnt, &key);
>   	if (value)
>   		*value += 1;
>   
> @@ -82,6 +82,20 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
>   	return bpf_redirect_map(&tx_port, vport, 0);
>   }
>   
> +SEC("xdp_devmap/map_prog")
> +int xdp_devmap_prog(struct xdp_md *ctx)
> +{
> +	long *value;
> +	u32 key = 1;
> +
> +	/* count packet in global counter */
> +	value = bpf_map_lookup_elem(&pktcnt, &key);
> +	if (value)
> +		*value += 1;
> +
> +	return XDP_PASS;
> +}
> +
>   /* Redirect require an XDP bpf_prog loaded on the TX device */
>   SEC("xdp_redirect_dummy")
>   int xdp_redirect_dummy_prog(struct xdp_md *ctx)
> diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
> index 35e16dee613e..8bdec0865e1d 100644
> --- a/samples/bpf/xdp_redirect_map_user.c
> +++ b/samples/bpf/xdp_redirect_map_user.c
> @@ -21,12 +21,13 @@
>   
>   static int ifindex_in;
>   static int ifindex_out;
> -static bool ifindex_out_xdp_dummy_attached = true;
> +static bool ifindex_out_xdp_dummy_attached = false;
> +static bool xdp_prog_attached = false;

Maybe xdp_devmap_prog_attached? Feel xdp_prog_attached
is too generic since actually it controls xdp_devmap program
attachment.

>   static __u32 prog_id;
>   static __u32 dummy_prog_id;
>   
>   static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
> -static int rxcnt_map_fd;
> +static int pktcnt_map_fd;
>   
>   static void int_exit(int sig)
>   {
> @@ -60,26 +61,46 @@ static void int_exit(int sig)
>   	exit(0);
>   }
>   
> -static void poll_stats(int interval, int ifindex)
> +static void poll_stats(int interval, int if_ingress, int if_egress)
>   {
>   	unsigned int nr_cpus = bpf_num_possible_cpus();
> -	__u64 values[nr_cpus], prev[nr_cpus];
> +	__u64 values[nr_cpus], in_prev[nr_cpus], e_prev[nr_cpus];
> +	__u64 sum;
> +	__u32 key;
> +	int i;
>   
> -	memset(prev, 0, sizeof(prev));
> +	memset(in_prev, 0, sizeof(in_prev));
> +	memset(e_prev, 0, sizeof(e_prev));
>   
>   	while (1) {
> -		__u64 sum = 0;
> -		__u32 key = 0;
> -		int i;
> +		sum = 0;
> +		key = 0;
>   
>   		sleep(interval);
> -		assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0);
> -		for (i = 0; i < nr_cpus; i++)
> -			sum += (values[i] - prev[i]);
> -		if (sum)
> -			printf("ifindex %i: %10llu pkt/s\n",
> -			       ifindex, sum / interval);
> -		memcpy(prev, values, sizeof(values));
> +		if (bpf_map_lookup_elem(pktcnt_map_fd, &key, values) == 0) {

When we could have a failure here? If it indeed failed maybe it signals
something wrong and the process should fail?

> +			for (i = 0; i < nr_cpus; i++)
> +				sum += (values[i] - in_prev[i]);
> +			if (sum)
> +				printf("in ifindex %i: %10llu pkt/s",
> +				       if_ingress, sum / interval);
> +			memcpy(in_prev, values, sizeof(values));
> +		}
> +
> +		if (!xdp_prog_attached) {
> +			printf("\n");
> +			continue;
> +		}
> +
> +		sum = 0;
> +		key = 1;
> +		if (bpf_map_lookup_elem(pktcnt_map_fd, &key, values) == 0) {

same as the above, if bpf_map_lookup_elem() failed, maybe we should 
signal a failure?

> +			for (i = 0; i < nr_cpus; i++)
> +				sum += (values[i] - e_prev[i]);
> +			if (sum)
> +				printf(", out ifindex %i: %10llu pkt/s\n",
> +				       if_egress, sum / interval);
> +			memcpy(e_prev, values, sizeof(values));
> +		}
>   	}
>   }
>   
[...]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-27  6:31   ` Yonghong Song
@ 2020-11-30  7:51     ` Hangbin Liu
  2020-11-30  9:32       ` Jesper Dangaard Brouer
  0 siblings, 1 reply; 33+ messages in thread
From: Hangbin Liu @ 2020-11-30  7:51 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, netdev, Daniel Borkmann, Jesper Dangaard Brouer,
	John Fastabend, Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski

On Thu, Nov 26, 2020 at 10:31:56PM -0800, Yonghong Song wrote:
> > index 35e16dee613e..8bdec0865e1d 100644
> > --- a/samples/bpf/xdp_redirect_map_user.c
> > +++ b/samples/bpf/xdp_redirect_map_user.c
> > @@ -21,12 +21,13 @@
> >   static int ifindex_in;
> >   static int ifindex_out;
> > -static bool ifindex_out_xdp_dummy_attached = true;
> > +static bool ifindex_out_xdp_dummy_attached = false;
> > +static bool xdp_prog_attached = false;
> 
> Maybe xdp_devmap_prog_attached? Feel xdp_prog_attached
> is too generic since actually it controls xdp_devmap program
> attachment.

Hi Yonghong,

Thanks for your comments. As Jesper replied, The 2nd xdp_prog on egress
doesn't tell us if the redirect was successful. So the number is meaningless.

I plan to write a example about vlan header modification based on egress
index. I will post the patch later.

Thanks
Hangbin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-30  7:51     ` Hangbin Liu
@ 2020-11-30  9:32       ` Jesper Dangaard Brouer
  2020-11-30 13:10         ` Hangbin Liu
  0 siblings, 1 reply; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2020-11-30  9:32 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: Yonghong Song, bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski, brouer

On Mon, 30 Nov 2020 15:51:07 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> On Thu, Nov 26, 2020 at 10:31:56PM -0800, Yonghong Song wrote:
> > > index 35e16dee613e..8bdec0865e1d 100644
> > > --- a/samples/bpf/xdp_redirect_map_user.c
> > > +++ b/samples/bpf/xdp_redirect_map_user.c
> > > @@ -21,12 +21,13 @@
> > >   static int ifindex_in;
> > >   static int ifindex_out;
> > > -static bool ifindex_out_xdp_dummy_attached = true;
> > > +static bool ifindex_out_xdp_dummy_attached = false;
> > > +static bool xdp_prog_attached = false;  
> > 
> > Maybe xdp_devmap_prog_attached? Feel xdp_prog_attached
> > is too generic since actually it controls xdp_devmap program
> > attachment.  
> 
> Hi Yonghong,
> 
> Thanks for your comments. As Jesper replied, The 2nd xdp_prog on egress
> doesn't tell us if the redirect was successful. So the number is meaningless.

Well, I would not say the counter is meaningless.  It true that 2nd
devmap xdp_prog doesn't tell us if the redirect was successful, which
means that your description/(understanding) of the counter was wrong.

I still think it is relevant to have a counter for packets processed by
this 2nd xdp_prog, just to make is visually clear that the 2nd xdp-prog
attached (to devmap entry) is running.  The point is that QA is using
these programs.

The lack of good output from this specific sample have cause many
bugzilla cases for me.  BZ cases that requires going back and forth a
number of times, before figuring out how the prog was (mis)used.  This
is why other samples like xdp_rxq_info and xdp_redirect_cpu have such a
verbose output, which in-practice have helped many times on QA issues.


> I plan to write a example about vlan header modification based on egress
> index. I will post the patch later.

I did notice the internal thread you had with Toke.  I still think it
will be more simple to modify the Ethernet mac addresses.  Adding a
VLAN id tag is more work, and will confuse benchmarks.  You are
increasing the packet size, which means that you NIC need to spend
slightly more time sending this packet (3.2 nanosec at 10Gbit/s), which
could falsely be interpreted as cost of 2nd devmap XDP-program.

(Details: these 3.2 ns will not be visible for smaller packets, because
the minimum Ethernet frame size will hide this, but I've experience this
problem with larger frames on real switch hardware (Juniper), where
ingress didn't have VLAN-tag and egress we added VLAN-tag with XDP, and
then switch buffer slowly increased until overflow).

As Alexei already pointed out, you assignment is to modify the packet
in the 2nd devmap XDP-prog.  Why: because you need to realize that this
will break your approach to multicast in your previous patchset.
(Yes, the offlist patch I gave you, that move running 2nd devmap
XDP-prog to a later stage, solved this packet-modify issue).

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-30  9:32       ` Jesper Dangaard Brouer
@ 2020-11-30 13:10         ` Hangbin Liu
  2020-11-30 15:12           ` Jesper Dangaard Brouer
  0 siblings, 1 reply; 33+ messages in thread
From: Hangbin Liu @ 2020-11-30 13:10 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: Yonghong Song, bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski

On Mon, Nov 30, 2020 at 10:32:08AM +0100, Jesper Dangaard Brouer wrote:
> > I plan to write a example about vlan header modification based on egress
> > index. I will post the patch later.
> 
> I did notice the internal thread you had with Toke.  I still think it
> will be more simple to modify the Ethernet mac addresses.  Adding a
> VLAN id tag is more work, and will confuse benchmarks.  You are

I plan to only modify the vlan id if there has. If you prefer to modify the
mac address, which way you'd like? Set src mac to egress interface's MAC?

> As Alexei already pointed out, you assignment is to modify the packet
> in the 2nd devmap XDP-prog.  Why: because you need to realize that this
> will break your approach to multicast in your previous patchset.
> (Yes, the offlist patch I gave you, that move running 2nd devmap
> XDP-prog to a later stage, solved this packet-modify issue).

BTW, it looks with your patch, the counter on egress would make more sense.
Should I add the counter after your patch posted?

Thanks
Hangbin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-30 13:10         ` Hangbin Liu
@ 2020-11-30 15:12           ` Jesper Dangaard Brouer
  2020-11-30 16:07             ` Toke Høiland-Jørgensen
  0 siblings, 1 reply; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2020-11-30 15:12 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: Yonghong Song, bpf, netdev, Daniel Borkmann, John Fastabend,
	Toke Høiland-Jørgensen, Tariq Toukan,
	Maciej Fijalkowski, brouer

On Mon, 30 Nov 2020 21:10:20 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> On Mon, Nov 30, 2020 at 10:32:08AM +0100, Jesper Dangaard Brouer wrote:
> > > I plan to write a example about vlan header modification based on egress
> > > index. I will post the patch later.  
> > 
> > I did notice the internal thread you had with Toke.  I still think it
> > will be more simple to modify the Ethernet mac addresses.  Adding a
> > VLAN id tag is more work, and will confuse benchmarks.  You are  
> 
> I plan to only modify the vlan id if there has. 

This sentence is not complete, but because of the internal thread I
know/assume that you mean, that you will only modify the vlan id if
there is already another VLAN tag in the packet. Let me express that
this is not good enough. This is not a feasible choice.

> If you prefer to modify the mac address, which way you'd like? Set
> src mac to egress interface's MAC?

Yes, that will be a good choice, to use the src mac from the egress
interface.  This would simulate part of what is needed for L3/routing.

Can I request that the dst mac is will be the incoming src mac?
Or if you are user-friendly add option that allows to set dst mac.

This is close to what swap-MAC (swap_src_dst_mac) is used for.  Let me
explain in more details, why this is practical.  It is practical
because then the Ethernet frame will be a valid frame that is received
by the sending interface.  Thus, if you redirect back same interface
(like XDP_TX, but testing xdp_do_redirect code) then you can check on
traffic generator if all frames were actually forwarded.  This is
exactly what the Red Hat performance team's Trex packet generator setup
does to validate and find the zero-loss generator rate.


> > As Alexei already pointed out, you assignment is to modify the packet
> > in the 2nd devmap XDP-prog.  Why: because you need to realize that this
> > will break your approach to multicast in your previous patchset.
> > (Yes, the offlist patch I gave you, that move running 2nd devmap
> > XDP-prog to a later stage, solved this packet-modify issue).  
> 
> BTW, it looks with your patch, the counter on egress would make more sense.
> Should I add the counter after your patch posted?

As I tried to explain.  Regardless, I want a counter that counts the
times the 2nd devmap attached XDP-program runs.  This is not a counter
that counts egress packets.  This is a counter that show that the 2nd
devmap attached XDP-program is running.  I don't know how to make this
more clear.

We do need ANOTHER counter that report how many packets are transmitted
on the egress device.  I'm thinking we can simply read:

 /sys/class/net/mlx5p1/statistics/tx_packets

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-30 15:12           ` Jesper Dangaard Brouer
@ 2020-11-30 16:07             ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 33+ messages in thread
From: Toke Høiland-Jørgensen @ 2020-11-30 16:07 UTC (permalink / raw)
  To: Jesper Dangaard Brouer, Hangbin Liu
  Cc: Yonghong Song, bpf, netdev, Daniel Borkmann, John Fastabend,
	Tariq Toukan, Maciej Fijalkowski, brouer

Jesper Dangaard Brouer <brouer@redhat.com> writes:

> On Mon, 30 Nov 2020 21:10:20 +0800
> Hangbin Liu <liuhangbin@gmail.com> wrote:
>
>> On Mon, Nov 30, 2020 at 10:32:08AM +0100, Jesper Dangaard Brouer wrote:
>> > > I plan to write a example about vlan header modification based on egress
>> > > index. I will post the patch later.  
>> > 
>> > I did notice the internal thread you had with Toke.  I still think it
>> > will be more simple to modify the Ethernet mac addresses.  Adding a
>> > VLAN id tag is more work, and will confuse benchmarks.  You are  
>> 
>> I plan to only modify the vlan id if there has. 
>
> This sentence is not complete, but because of the internal thread I
> know/assume that you mean, that you will only modify the vlan id if
> there is already another VLAN tag in the packet. Let me express that
> this is not good enough. This is not a feasible choice.
>
>> If you prefer to modify the mac address, which way you'd like? Set
>> src mac to egress interface's MAC?
>
> Yes, that will be a good choice, to use the src mac from the egress
> interface.  This would simulate part of what is needed for L3/routing.
>
> Can I request that the dst mac is will be the incoming src mac?
> Or if you are user-friendly add option that allows to set dst mac.

One issue with this is that I think it would be neat if we could output
the egress ifindex as part of the packet data, to verify that different
packets can get different content (in the multicast case). If we just
modify the MAC address this is difficult. I guess we could just decide
to step on one byte in the src MAC or something, but VLAN tags seemed
like an obvious alternative :)

-Toke


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv3 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-11-26  8:43 ` [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map Hangbin Liu
  2020-11-26 10:51   ` Jesper Dangaard Brouer
  2020-11-27  6:31   ` Yonghong Song
@ 2020-12-08  8:18   ` Hangbin Liu
  2020-12-08 10:39     ` Jesper Dangaard Brouer
  2020-12-08 12:01     ` [PATCHv4 " Hangbin Liu
  2 siblings, 2 replies; 33+ messages in thread
From: Hangbin Liu @ 2020-12-08  8:18 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  60 ++++++++++-
 samples/bpf/xdp_redirect_map_user.c | 153 ++++++++++++++++++++--------
 2 files changed, 168 insertions(+), 45 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..6b2164722649 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to stroe egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -79,7 +96,40 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+SEC("xdp_devmap/map_prog")
+int xdp_redirect_map_egress(struct xdp_md *ctx)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	__be64 *mac;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	mac = bpf_map_lookup_elem(&tx_mac, &key);
+	if (mac)
+		__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..19636045c8dc 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -21,7 +25,8 @@
 
 static int ifindex_in;
 static int ifindex_out;
-static bool ifindex_out_xdp_dummy_attached = true;
+static bool ifindex_out_xdp_dummy_attached = false;
+static bool xdp_devmap_attached = false;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,29 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	struct ifreq ifr;
+	char ifname[IF_NAMESIZE];
+	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+	if (fd < 0)
+		return -1;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		return -1;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		return -1;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	close(fd);
+
+	return 0;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +118,26 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int prog_fd, dummy_prog_fd, devmap_prog_fd = -1;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,6 +150,9 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
@@ -150,67 +183,107 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
-	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
-		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
-		return 1;
+	prog_fd = bpf_program__fd(prog);
+	if (!prog || prog_fd < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/tx_port_map in obj file failed\n");
+		goto out;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
+	if (rxcnt_map_fd < 0 || tx_mac_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
-		return 1;
+		goto out;
 	}
 
 	if (bpf_set_link_xdp_fd(ifindex_in, prog_fd, xdp_flags) < 0) {
 		printf("ERROR: link set xdp fd failed on %d\n", ifindex_in);
-		return 1;
+		goto out;
 	}
 
 	ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
 	if (ret) {
 		printf("can't get prog info - %s\n", strerror(errno));
-		return ret;
+		goto out;
 	}
 	prog_id = info.id;
 
-	/* Loading dummy XDP prog on out-device */
-	if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
-			    (xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) < 0) {
-		printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
-		ifindex_out_xdp_dummy_attached = false;
-	}
+	/* If -X supplied, load 2nd xdp prog on egress.
+	 * If not, just load dummy prog on egress.
+	 */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
 
-	memset(&info, 0, sizeof(info));
-	ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
-	if (ret) {
-		printf("can't get prog info - %s\n", strerror(errno));
-		return ret;
+		devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	} else if (ifindex_in != ifindex_out) {
+		dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
+		if (!dummy_prog) {
+			printf("finding dummy_prog in obj file failed\n");
+			goto out;
+		}
+
+		dummy_prog_fd = bpf_program__fd(dummy_prog);
+		if (dummy_prog_fd < 0) {
+			printf("find dummy_prog fd failed\n");
+			goto out;
+		}
+
+		if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
+					(xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) == 0) {
+			ifindex_out_xdp_dummy_attached = true;
+		} else {
+			printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
+		}
+
+		memset(&info, 0, sizeof(info));
+		ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
+		if (ret) {
+			printf("can't get prog info - %s\n", strerror(errno));
+		}
+		dummy_prog_id = info.id;
 	}
-	dummy_prog_id = info.id;
 
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
-		perror("bpf_update_elem");
+		perror("bpf_update_elem tx_port_map_fd");
 		goto out;
 	}
 
 	poll_stats(2, ifindex_out);
 
 out:
-	return 0;
+	bpf_object__close(obj);
+	return 1;
 }
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv3 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-12-08  8:18   ` [PATCHv3 " Hangbin Liu
@ 2020-12-08 10:39     ` Jesper Dangaard Brouer
  2020-12-08 11:11       ` Hangbin Liu
  2020-12-08 12:01     ` [PATCHv4 " Hangbin Liu
  1 sibling, 1 reply; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2020-12-08 10:39 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen, brouer

On Tue,  8 Dec 2020 16:18:56 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> ---
> v3:
> a) modify the src mac address based on egress mac
> 
> v2:
> a) use pkt counter instead of IP ttl modification on egress program
> b) make the egress program selectable by option -X
> ---
>  samples/bpf/xdp_redirect_map_kern.c |  60 ++++++++++-
>  samples/bpf/xdp_redirect_map_user.c | 153 ++++++++++++++++++++--------
>  2 files changed, 168 insertions(+), 45 deletions(-)
> 

[...]
> diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
> index 31131b6e7782..19636045c8dc 100644
> --- a/samples/bpf/xdp_redirect_map_user.c
> +++ b/samples/bpf/xdp_redirect_map_user.c
> @@ -14,6 +14,10 @@
>  #include <unistd.h>
>  #include <libgen.h>
>  #include <sys/resource.h>
> +#include <sys/ioctl.h>
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
>  
>  #include "bpf_util.h"
>  #include <bpf/bpf.h>
> @@ -21,7 +25,8 @@
>  
>  static int ifindex_in;
>  static int ifindex_out;
> -static bool ifindex_out_xdp_dummy_attached = true;
> +static bool ifindex_out_xdp_dummy_attached = false;
> +static bool xdp_devmap_attached = false;
>  static __u32 prog_id;
>  static __u32 dummy_prog_id;
>  
> @@ -83,6 +88,29 @@ static void poll_stats(int interval, int ifindex)
>  	}
>  }
>  
> +static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
> +{
> +	struct ifreq ifr;
> +	char ifname[IF_NAMESIZE];
> +	int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);

I would have expected (like ethtool):
 fd = socket(AF_INET, SOCK_DGRAM, 0);

> +	if (fd < 0)
> +		return -1;
> +
> +	if (!if_indextoname(ifindex_out, ifname))
> +		return -1;
> +
> +	strcpy(ifr.ifr_name, ifname);
> +
> +	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
> +		return -1;
> +
> +	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
> +	close(fd);
> +
> +	return 0;
> +}
[...]

> -	/* Loading dummy XDP prog on out-device */
> -	if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
> -			    (xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) < 0) {
> -		printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
> -		ifindex_out_xdp_dummy_attached = false;
> -	}
> +	/* If -X supplied, load 2nd xdp prog on egress.
> +	 * If not, just load dummy prog on egress.
> +	 */

The dummy prog need to be loaded, regardless of 2nd xdp prog on egress.


> +	if (xdp_devmap_attached) {
> +		unsigned char mac_addr[6];
>  
> -	memset(&info, 0, sizeof(info));
> -	ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
> -	if (ret) {
> -		printf("can't get prog info - %s\n", strerror(errno));
> -		return ret;
> +		devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog");
> +		if (!devmap_prog) {
> +			printf("finding devmap_prog in obj file failed\n");
> +			goto out;
> +		}
> +		devmap_prog_fd = bpf_program__fd(devmap_prog);
> +		if (devmap_prog_fd < 0) {
> +			printf("finding devmap_prog fd failed\n");
> +			goto out;
> +		}
> +
> +		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
> +			printf("get interface %d mac failed\n", ifindex_out);
> +			goto out;
> +		}
> +
> +		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
> +		if (ret) {
> +			perror("bpf_update_elem tx_mac_map_fd");
> +			goto out;
> +		}
> +	} else if (ifindex_in != ifindex_out) {
> +		dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
> +		if (!dummy_prog) {
> +			printf("finding dummy_prog in obj file failed\n");
> +			goto out;
> +		}
> +
> +		dummy_prog_fd = bpf_program__fd(dummy_prog);
> +		if (dummy_prog_fd < 0) {
> +			printf("find dummy_prog fd failed\n");
> +			goto out;
> +		}
> +
> +		if (bpf_set_link_xdp_fd(ifindex_out, dummy_prog_fd,
> +					(xdp_flags | XDP_FLAGS_UPDATE_IF_NOEXIST)) == 0) {
> +			ifindex_out_xdp_dummy_attached = true;
> +		} else {
> +			printf("WARN: link set xdp fd failed on %d\n", ifindex_out);
> +		}
> +
> +		memset(&info, 0, sizeof(info));
> +		ret = bpf_obj_get_info_by_fd(dummy_prog_fd, &info, &info_len);
> +		if (ret) {
> +			printf("can't get prog info - %s\n", strerror(errno));
> +		}
> +		dummy_prog_id = info.id;
>  	}
> -	dummy_prog_id = info.id;
>  
>  	signal(SIGINT, int_exit);
>  	signal(SIGTERM, int_exit);
>  
> -	/* populate virtual to physical port map */
> -	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
> +	devmap_val.ifindex = ifindex_out;
> +	devmap_val.bpf_prog.fd = devmap_prog_fd;
> +	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
>  	if (ret) {
> -		perror("bpf_update_elem");
> +		perror("bpf_update_elem tx_port_map_fd");
>  		goto out;
>  	}
>  
>  	poll_stats(2, ifindex_out);
>  
>  out:
> -	return 0;
> +	bpf_object__close(obj);
> +	return 1;
>  }



-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv3 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-12-08 10:39     ` Jesper Dangaard Brouer
@ 2020-12-08 11:11       ` Hangbin Liu
  0 siblings, 0 replies; 33+ messages in thread
From: Hangbin Liu @ 2020-12-08 11:11 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen

On Tue, Dec 08, 2020 at 11:39:14AM +0100, Jesper Dangaard Brouer wrote:
> > +	/* If -X supplied, load 2nd xdp prog on egress.
> > +	 * If not, just load dummy prog on egress.
> > +	 */
> 
> The dummy prog need to be loaded, regardless of 2nd xdp prog on egress.

Thanks for this remind, Now I know why the pkts are dropped with I do perf
test on physical NICs.

Regards
Hangbin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv4 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-12-08  8:18   ` [PATCHv3 " Hangbin Liu
  2020-12-08 10:39     ` Jesper Dangaard Brouer
@ 2020-12-08 12:01     ` Hangbin Liu
  2020-12-11  0:15       ` Daniel Borkmann
  2020-12-11  2:40       ` [PATCHv5 " Hangbin Liu
  1 sibling, 2 replies; 33+ messages in thread
From: Hangbin Liu @ 2020-12-08 12:01 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
v4:
a) Update get_mac_addr socket creation
b) Load dummy prog regardless of 2nd xdp prog on egress

v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  60 ++++++++++++++--
 samples/bpf/xdp_redirect_map_user.c | 104 +++++++++++++++++++++++-----
 2 files changed, 140 insertions(+), 24 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..6b2164722649 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to stroe egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -79,7 +96,40 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+SEC("xdp_devmap/map_prog")
+int xdp_redirect_map_egress(struct xdp_md *ctx)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	__be64 *mac;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	mac = bpf_map_lookup_elem(&tx_mac, &key);
+	if (mac)
+		__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..9866d759bd11 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -22,6 +26,7 @@
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static bool xdp_devmap_attached = false;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,29 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	struct ifreq ifr;
+	char ifname[IF_NAMESIZE];
+	int fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+	if (fd < 0)
+		return -1;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		return -1;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		return -1;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	close(fd);
+
+	return 0;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +118,26 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int prog_fd, dummy_prog_fd, devmap_prog_fd = -1;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,6 +150,9 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
@@ -150,24 +183,28 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
+	dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
+	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/tx_port_map in obj file failed\n");
+		goto out;
+	}
+	prog_fd = bpf_program__fd(prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -199,11 +236,40 @@ int main(int argc, char **argv)
 	}
 	dummy_prog_id = info.id;
 
+	/* Load 2nd xdp prog on egress. */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
+
+		devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	}
+
+
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv4 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-12-08 12:01     ` [PATCHv4 " Hangbin Liu
@ 2020-12-11  0:15       ` Daniel Borkmann
  2020-12-11  2:40       ` [PATCHv5 " Hangbin Liu
  1 sibling, 0 replies; 33+ messages in thread
From: Daniel Borkmann @ 2020-12-11  0:15 UTC (permalink / raw)
  To: Hangbin Liu, bpf
  Cc: netdev, Jesper Dangaard Brouer, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen

On 12/8/20 1:01 PM, Hangbin Liu wrote:
[...]
>   
> +static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
> +{
> +	struct ifreq ifr;
> +	char ifname[IF_NAMESIZE];
> +	int fd = socket(AF_INET, SOCK_DGRAM, 0);
> +
> +	if (fd < 0)
> +		return -1;
> +
> +	if (!if_indextoname(ifindex_out, ifname))
> +		return -1;
> +
> +	strcpy(ifr.ifr_name, ifname);
> +
> +	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
> +		return -1;
> +
> +	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
> +	close(fd);

Given we do bother to close the socket fd here, we should also close it in above
error cases..

> +	return 0;
> +}
> +

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv5 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-12-08 12:01     ` [PATCHv4 " Hangbin Liu
  2020-12-11  0:15       ` Daniel Borkmann
@ 2020-12-11  2:40       ` Hangbin Liu
  2021-01-14 14:27         ` [PATCHv6 " Hangbin Liu
  1 sibling, 1 reply; 33+ messages in thread
From: Hangbin Liu @ 2020-12-11  2:40 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
v5:
a) close fd when err out in get_mac_addr()
b) exit program when both -S and -X supplied.

v4:
a) Update get_mac_addr socket create
b) Load dummy prog regardless of 2nd xdp prog on egress

v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  60 +++++++++++++--
 samples/bpf/xdp_redirect_map_user.c | 113 +++++++++++++++++++++++-----
 2 files changed, 148 insertions(+), 25 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..6b2164722649 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to stroe egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -79,7 +96,40 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+SEC("xdp_devmap/map_prog")
+int xdp_redirect_map_egress(struct xdp_md *ctx)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	__be64 *mac;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	mac = bpf_map_lookup_elem(&tx_mac, &key);
+	if (mac)
+		__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..a5a70df215c3 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -22,6 +26,7 @@
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static bool xdp_devmap_attached = false;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,32 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	char ifname[IF_NAMESIZE];
+	struct ifreq ifr;
+	int fd, ret = -1;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return ret;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		goto err_out;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		goto err_out;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	ret = 0;
+
+err_out:
+	close(fd);
+	return ret;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +121,26 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int prog_fd, dummy_prog_fd, devmap_prog_fd = -1;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,14 +153,21 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
 		}
 	}
 
-	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 		xdp_flags |= XDP_FLAGS_DRV_MODE;
+	} else if (xdp_devmap_attached) {
+		printf("Load xdp program on egress with SKB mode not supported yet\n");
+		return 1;
+	}
 
 	if (optind == argc) {
 		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +190,28 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
+	}
+	dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
+	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/tx_port_map in obj file failed\n");
+		goto out;
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
+	prog_fd = bpf_program__fd(prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -199,11 +243,40 @@ int main(int argc, char **argv)
 	}
 	dummy_prog_id = info.id;
 
+	/* Load 2nd xdp prog on egress. */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
+
+		devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	}
+
+
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCHv6 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2020-12-11  2:40       ` [PATCHv5 " Hangbin Liu
@ 2021-01-14 14:27         ` Hangbin Liu
  2021-01-14 21:01           ` Yonghong Song
  2021-01-15  6:24           ` [PATCHv7 " Hangbin Liu
  0 siblings, 2 replies; 33+ messages in thread
From: Hangbin Liu @ 2021-01-14 14:27 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

---
v6: no code update, only rebase the code on latest bpf-next

v5:
a) close fd when err out in get_mac_addr()
b) exit program when both -S and -X supplied.

v4:
a) Update get_mac_addr socket create
b) Load dummy prog regardless of 2nd xdp prog on egress

v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  75 ++++++++++++++--
 samples/bpf/xdp_redirect_map_user.c | 135 +++++++++++++++++++++++-----
 2 files changed, 184 insertions(+), 26 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..8b8e73d25ad6 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to stroe egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -73,13 +90,59 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 
 	/* count packet in global counter */
 	value = bpf_map_lookup_elem(&rxcnt, &key);
-	if (value)
+	if (value) {
 		*value += 1;
+		if (*value % 2 == 1)
+			vport = 1;
+	}
 
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+static int xdp_redirect_map_egress(struct xdp_md *ctx, unsigned char *mac)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
+}
+
+SEC("xdp_devmap/map_prog_0")
+int xdp_redirect_map_egress_0(struct xdp_md *ctx)
+{
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
+	return xdp_redirect_map_egress(ctx, mac);
+}
+
+SEC("xdp_devmap/map_prog_1")
+int xdp_redirect_map_egress_1(struct xdp_md *ctx)
+{
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x1, 0x1};
+	return xdp_redirect_map_egress(ctx, mac);
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..9a778f7c45ff 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -22,6 +26,7 @@
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static bool xdp_devmap_attached = false;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,32 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	char ifname[IF_NAMESIZE];
+	struct ifreq ifr;
+	int fd, ret = -1;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return ret;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		goto err_out;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		goto err_out;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	ret = 0;
+
+err_out:
+	close(fd);
+	return ret;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +121,27 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int devmap_prog_fd_0 = -1, devmap_prog_fd_1 = -1;
+	int prog_fd, dummy_prog_fd;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,14 +154,21 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
 		}
 	}
 
-	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 		xdp_flags |= XDP_FLAGS_DRV_MODE;
+	} else if (xdp_devmap_attached) {
+		printf("Load xdp program on egress with SKB mode not supported yet\n");
+		return 1;
+	}
 
 	if (optind == argc) {
 		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +191,28 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
+	}
+	dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
+	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/tx_port_map in obj file failed\n");
+		goto out;
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
+	prog_fd = bpf_program__fd(prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -199,11 +244,61 @@ int main(int argc, char **argv)
 	}
 	dummy_prog_id = info.id;
 
+	/* Load 2nd xdp prog on egress. */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
+
+		devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog_0");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd_0 = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd_0 < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		devmap_prog = bpf_object__find_program_by_title(obj, "xdp_devmap/map_prog_1");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd_1 = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd_1 < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	}
+
+
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	key = 0;
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd_0;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
+	if (ret) {
+		perror("bpf_update_elem");
+		goto out;
+	}
+
+	key = 1;
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd_1;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv6 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-14 14:27         ` [PATCHv6 " Hangbin Liu
@ 2021-01-14 21:01           ` Yonghong Song
  2021-01-15  4:17             ` Hangbin Liu
  2021-01-15  6:24           ` [PATCHv7 " Hangbin Liu
  1 sibling, 1 reply; 33+ messages in thread
From: Yonghong Song @ 2021-01-14 21:01 UTC (permalink / raw)
  To: Hangbin Liu, bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Toke Høiland-Jørgensen



On 1/14/21 6:27 AM, Hangbin Liu wrote:
> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> 
> ---
> v6: no code update, only rebase the code on latest bpf-next
> 
> v5:
> a) close fd when err out in get_mac_addr()
> b) exit program when both -S and -X supplied.
> 
> v4:
> a) Update get_mac_addr socket create
> b) Load dummy prog regardless of 2nd xdp prog on egress
> 
> v3:
> a) modify the src mac address based on egress mac
> 
> v2:
> a) use pkt counter instead of IP ttl modification on egress program
> b) make the egress program selectable by option -X
> ---
>   samples/bpf/xdp_redirect_map_kern.c |  75 ++++++++++++++--
>   samples/bpf/xdp_redirect_map_user.c | 135 +++++++++++++++++++++++-----
>   2 files changed, 184 insertions(+), 26 deletions(-)
> 
> diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
> index 6489352ab7a4..8b8e73d25ad6 100644
> --- a/samples/bpf/xdp_redirect_map_kern.c
> +++ b/samples/bpf/xdp_redirect_map_kern.c
> @@ -19,12 +19,22 @@
>   #include <linux/ipv6.h>
>   #include <bpf/bpf_helpers.h>
>   
> +/* The 2nd xdp prog on egress does not support skb mode, so we define two
> + * maps, tx_port_general and tx_port_native.
> + */
>   struct {
>   	__uint(type, BPF_MAP_TYPE_DEVMAP);
>   	__uint(key_size, sizeof(int));
>   	__uint(value_size, sizeof(int));
>   	__uint(max_entries, 100);
> -} tx_port SEC(".maps");
> +} tx_port_general SEC(".maps");
> +
> +struct {
> +	__uint(type, BPF_MAP_TYPE_DEVMAP);
> +	__uint(key_size, sizeof(int));
> +	__uint(value_size, sizeof(struct bpf_devmap_val));
> +	__uint(max_entries, 100);
> +} tx_port_native SEC(".maps");
>   
>   /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
>    * feedback.  Redirect TX errors can be caught via a tracepoint.
> @@ -36,6 +46,14 @@ struct {
>   	__uint(max_entries, 1);
>   } rxcnt SEC(".maps");
>   
> +/* map to stroe egress interface mac address */

s/stroe/store

> +struct {
> +	__uint(type, BPF_MAP_TYPE_ARRAY);
> +	__type(key, u32);
> +	__type(value, __be64);
> +	__uint(max_entries, 1);
> +} tx_mac SEC(".maps");
> +
>   static void swap_src_dst_mac(void *data)
>   {
>   	unsigned short *p = data;
> @@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
>   	p[5] = dst[2];
>   }
>   
[...]
>   int main(int argc, char **argv)
>   {
>   	struct bpf_prog_load_attr prog_load_attr = {
> -		.prog_type	= BPF_PROG_TYPE_XDP,
> +		.prog_type	= BPF_PROG_TYPE_UNSPEC,
>   	};
> -	struct bpf_program *prog, *dummy_prog;
> +	struct bpf_program *prog, *dummy_prog, *devmap_prog;
> +	int devmap_prog_fd_0 = -1, devmap_prog_fd_1 = -1;

The default value is -1 here. I remembered there was a discussion
about the default value here, does default value 0 work here?

> +	int prog_fd, dummy_prog_fd;
> +	int tx_port_map_fd, tx_mac_map_fd;
> +	struct bpf_devmap_val devmap_val;
>   	struct bpf_prog_info info = {};
>   	__u32 info_len = sizeof(info);
> -	int prog_fd, dummy_prog_fd;
> -	const char *optstr = "FSN";
> +	const char *optstr = "FSNX";
>   	struct bpf_object *obj;
>   	int ret, opt, key = 0;
>   	char filename[256];
> -	int tx_port_map_fd;
>   
>   	while ((opt = getopt(argc, argv, optstr)) != -1) {
>   		switch (opt) {
> @@ -120,14 +154,21 @@ int main(int argc, char **argv)
>   		case 'F':
>   			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
>   			break;
> +		case 'X':
> +			xdp_devmap_attached = true;
> +			break;
>   		default:
>   			usage(basename(argv[0]));
>   			return 1;
>   		}
>   	}
>   
> -	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
> +	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
>   		xdp_flags |= XDP_FLAGS_DRV_MODE;
> +	} else if (xdp_devmap_attached) {
> +		printf("Load xdp program on egress with SKB mode not supported yet\n");
> +		return 1;
> +	}
>   
>   	if (optind == argc) {
>   		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
> @@ -150,24 +191,28 @@ int main(int argc, char **argv)
>   	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
>   		return 1;
>   
> -	prog = bpf_program__next(NULL, obj);
> -	dummy_prog = bpf_program__next(prog, obj);
> -	if (!prog || !dummy_prog) {
> -		printf("finding a prog in obj file failed\n");
> -		return 1;
> +	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
> +		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_general");

libbpf supports each section having multiple programs, so 
bpf_object__find_program_by_title() is not recommended.
Could you change to bpf_object__find_program_by_name()?

> +		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
> +	} else {
> +		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_native");
> +		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
> +	}
> +	dummy_prog = bpf_object__find_program_by_title(obj, "xdp_redirect_dummy");
> +	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
> +		printf("finding prog/tx_port_map in obj file failed\n");
> +		goto out;
>   	}
> -	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
> -	 * so we're missing only the fd for dummy prog
> -	 */
> +	prog_fd = bpf_program__fd(prog);
>   	dummy_prog_fd = bpf_program__fd(dummy_prog);
> -	if (prog_fd < 0 || dummy_prog_fd < 0) {
> +	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
>   		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
>   		return 1;
>   	}
>   
> -	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
> +	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
>   	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
> -	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
> +	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
>   		printf("bpf_object__find_map_fd_by_name failed\n");
>   		return 1;
>   	}
[...]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv6 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-14 21:01           ` Yonghong Song
@ 2021-01-15  4:17             ` Hangbin Liu
  0 siblings, 0 replies; 33+ messages in thread
From: Hangbin Liu @ 2021-01-15  4:17 UTC (permalink / raw)
  To: Yonghong Song
  Cc: bpf, netdev, Daniel Borkmann, Jesper Dangaard Brouer,
	John Fastabend, Toke Høiland-Jørgensen

Hi Yonghong,
On Thu, Jan 14, 2021 at 01:01:28PM -0800, Yonghong Song wrote:
> > +/* map to stroe egress interface mac address */
> 
> s/stroe/store

Thanks, I will fix it.
> > -	struct bpf_program *prog, *dummy_prog;
> > +	struct bpf_program *prog, *dummy_prog, *devmap_prog;
> > +	int devmap_prog_fd_0 = -1, devmap_prog_fd_1 = -1;
> 
> The default value is -1 here. I remembered there was a discussion
> about the default value here, does default value 0 work here?

I didn't saw the discussion. But 0 should works as in __dev_map_alloc_node
it only gets the prog when fd > 0.

static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net,
                                                    struct bpf_dtab *dtab,
                                                    struct bpf_devmap_val *val,
                                                    unsigned int idx)
{
	...
	if (val->bpf_prog.fd > 0) {
		prog = bpf_prog_get_type_dev(val->bpf_prog.fd,
				BPF_PROG_TYPE_XDP, false);
		if (IS_ERR(prog))
			goto err_put_dev;
		if (prog->expected_attach_type != BPF_XDP_DEVMAP)
			goto err_put_prog;
	}
	...
}

> > +	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
> > +		prog = bpf_object__find_program_by_title(obj, "xdp_redirect_general");
> 
> libbpf supports each section having multiple programs, so
> bpf_object__find_program_by_title() is not recommended.
> Could you change to bpf_object__find_program_by_name()?

Thanks for this reminder, I will fix it.

Thanks
Hangbin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv7 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-14 14:27         ` [PATCHv6 " Hangbin Liu
  2021-01-14 21:01           ` Yonghong Song
@ 2021-01-15  6:24           ` Hangbin Liu
  2021-01-15 16:57             ` Yonghong Song
                               ` (2 more replies)
  1 sibling, 3 replies; 33+ messages in thread
From: Hangbin Liu @ 2021-01-15  6:24 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

---
v7:
a) use bpf_object__find_program_by_name() instad of
   bpf_object__find_program_by_title()
b) set default devmap fd to 0

v6: no code update, only rebase the code on latest bpf-next

v5:
a) close fd when err out in get_mac_addr()
b) exit program when both -S and -X supplied.

v4:
a) Update get_mac_addr socket create
b) Load dummy prog regardless of 2nd xdp prog on egress

v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  77 +++++++++++++--
 samples/bpf/xdp_redirect_map_user.c | 141 ++++++++++++++++++++++++----
 2 files changed, 192 insertions(+), 26 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..1a3bac75ba81 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to store egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -73,13 +90,61 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 
 	/* count packet in global counter */
 	value = bpf_map_lookup_elem(&rxcnt, &key);
-	if (value)
+	if (value) {
 		*value += 1;
+		if (*value % 2 == 1)
+			vport = 1;
+	}
 
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+static int xdp_redirect_map_egress(struct xdp_md *ctx, unsigned char *mac)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+/* This program will set src mac to 00:00:00:00:00:01 */
+SEC("xdp_devmap/map_prog_0")
+int xdp_redirect_map_egress_0(struct xdp_md *ctx)
+{
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
+	return xdp_redirect_map_egress(ctx, mac);
+}
+
+/* This program will set src mac to 00:00:00:00:01:01 */
+SEC("xdp_devmap/map_prog_1")
+int xdp_redirect_map_egress_1(struct xdp_md *ctx)
+{
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x1, 0x1};
+	return xdp_redirect_map_egress(ctx, mac);
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..c2c06aa2ecad 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -22,6 +26,7 @@
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static bool xdp_devmap_attached = false;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,32 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	char ifname[IF_NAMESIZE];
+	struct ifreq ifr;
+	int fd, ret = -1;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return ret;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		goto err_out;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		goto err_out;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	ret = 0;
+
+err_out:
+	close(fd);
+	return ret;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +121,27 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int devmap_prog_fd_0 = 0, devmap_prog_fd_1 = 0;
+	int prog_fd, dummy_prog_fd;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,14 +154,21 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
 		}
 	}
 
-	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 		xdp_flags |= XDP_FLAGS_DRV_MODE;
+	} else if (xdp_devmap_attached) {
+		printf("Load xdp program on egress with SKB mode not supported yet\n");
+		return 1;
+	}
 
 	if (optind == argc) {
 		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +191,28 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
+	dummy_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_dummy_prog");
+	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/dummy_prog/tx_port_map in obj file failed\n");
+		goto out;
+	}
+	prog_fd = bpf_program__fd(prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -199,11 +244,67 @@ int main(int argc, char **argv)
 	}
 	dummy_prog_id = info.id;
 
+	/* Load 2nd xdp prog on egress. */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
+
+		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress_0");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd_0 = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd_0 < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress_1");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd_1 = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd_1 < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	}
+
+
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	/* devmap prog 0 will set src mac to 00:00:00:00:00:01
+	 * if 2nd xdp prog attached on egress
+	 */
+	key = 0;
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd_0;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
+	if (ret) {
+		perror("bpf_update_elem");
+		goto out;
+	}
+
+	/* devmap prog 1 will set src mac to 00:00:00:00:01:01
+	 * if 2nd xdp prog attached on egress
+	 */
+	key = 1;
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd_1;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv7 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-15  6:24           ` [PATCHv7 " Hangbin Liu
@ 2021-01-15 16:57             ` Yonghong Song
  2021-01-18 22:46             ` Daniel Borkmann
  2021-01-19  3:12             ` [PATCHv8 " Hangbin Liu
  2 siblings, 0 replies; 33+ messages in thread
From: Yonghong Song @ 2021-01-15 16:57 UTC (permalink / raw)
  To: Hangbin Liu, bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Toke Høiland-Jørgensen



On 1/14/21 10:24 PM, Hangbin Liu wrote:
> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

Acked-by: Yonghong Song <yhs@fb.com>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv7 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-15  6:24           ` [PATCHv7 " Hangbin Liu
  2021-01-15 16:57             ` Yonghong Song
@ 2021-01-18 22:46             ` Daniel Borkmann
  2021-01-19  3:12             ` [PATCHv8 " Hangbin Liu
  2 siblings, 0 replies; 33+ messages in thread
From: Daniel Borkmann @ 2021-01-18 22:46 UTC (permalink / raw)
  To: Hangbin Liu, bpf
  Cc: netdev, Jesper Dangaard Brouer, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen

On 1/15/21 7:24 AM, Hangbin Liu wrote:
> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

This has a bunch of minor checkpatch issues, see also netdev/checkpatch	[0];
disregard the 'exceeds 80 columns' but there is a double newline and other
minor stuff that slipped in (usually good to catch before submission):

WARNING: Missing a blank line after declarations
#133: FILE: samples/bpf/xdp_redirect_map_kern.c:139:
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
+	return xdp_redirect_map_egress(ctx, mac);

WARNING: Missing a blank line after declarations
#141: FILE: samples/bpf/xdp_redirect_map_kern.c:147:
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x1, 0x1};
+	return xdp_redirect_map_egress(ctx, mac);

ERROR: do not initialise statics to false
#164: FILE: samples/bpf/xdp_redirect_map_user.c:29:
+static bool xdp_devmap_attached = false;

CHECK: Please don't use multiple blank lines
#340: FILE: samples/bpf/xdp_redirect_map_user.c:285:
+
+

Please carry ACK forward, fix them up and resubmit, thanks!

   [0] https://patchwork.kernel.org/project/netdevbpf/patch/20210115062433.2624893-1-liuhangbin@gmail.com/

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv8 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-15  6:24           ` [PATCHv7 " Hangbin Liu
  2021-01-15 16:57             ` Yonghong Song
  2021-01-18 22:46             ` Daniel Borkmann
@ 2021-01-19  3:12             ` Hangbin Liu
  2021-01-19 14:51               ` Jesper Dangaard Brouer
  2021-01-21 13:06               ` [PATCHv9 " Hangbin Liu
  2 siblings, 2 replies; 33+ messages in thread
From: Hangbin Liu @ 2021-01-19  3:12 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

---
v8: Fix some checkpatch issues.

v7:
a) use bpf_object__find_program_by_name() instad of
   bpf_object__find_program_by_title()
b) set default devmap fd to 0

v6: no code update, only rebase the code on latest bpf-next

v5:
a) close fd when err out in get_mac_addr()
b) exit program when both -S and -X supplied.

v4:
a) Update get_mac_addr socket create
b) Load dummy prog regardless of 2nd xdp prog on egress

v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  79 ++++++++++++++--
 samples/bpf/xdp_redirect_map_user.c | 140 ++++++++++++++++++++++++----
 2 files changed, 193 insertions(+), 26 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..e5c0d07a82d8 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to store egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -73,13 +90,63 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 
 	/* count packet in global counter */
 	value = bpf_map_lookup_elem(&rxcnt, &key);
-	if (value)
+	if (value) {
 		*value += 1;
+		if (*value % 2 == 1)
+			vport = 1;
+	}
 
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+static int xdp_redirect_map_egress(struct xdp_md *ctx, unsigned char *mac)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+/* This program will set src mac to 00:00:00:00:00:01 */
+SEC("xdp_devmap/map_prog_0")
+int xdp_redirect_map_egress_0(struct xdp_md *ctx)
+{
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
+
+	return xdp_redirect_map_egress(ctx, mac);
+}
+
+/* This program will set src mac to 00:00:00:00:01:01 */
+SEC("xdp_devmap/map_prog_1")
+int xdp_redirect_map_egress_1(struct xdp_md *ctx)
+{
+	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x1, 0x1};
+
+	return xdp_redirect_map_egress(ctx, mac);
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..99c3bdea3e49 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -22,6 +26,7 @@
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static bool xdp_devmap_attached;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,32 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	char ifname[IF_NAMESIZE];
+	struct ifreq ifr;
+	int fd, ret = -1;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return ret;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		goto err_out;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		goto err_out;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	ret = 0;
+
+err_out:
+	close(fd);
+	return ret;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +121,27 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int devmap_prog_fd_0 = 0, devmap_prog_fd_1 = 0;
+	int prog_fd, dummy_prog_fd;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,14 +154,21 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
 		}
 	}
 
-	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 		xdp_flags |= XDP_FLAGS_DRV_MODE;
+	} else if (xdp_devmap_attached) {
+		printf("Load xdp program on egress with SKB mode not supported yet\n");
+		return 1;
+	}
 
 	if (optind == argc) {
 		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +191,28 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
+	dummy_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_dummy_prog");
+	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/dummy_prog/tx_port_map in obj file failed\n");
+		goto out;
+	}
+	prog_fd = bpf_program__fd(prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -199,11 +244,66 @@ int main(int argc, char **argv)
 	}
 	dummy_prog_id = info.id;
 
+	/* Load 2nd xdp prog on egress. */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
+
+		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress_0");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd_0 = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd_0 < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress_1");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd_1 = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd_1 < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	}
+
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	/* devmap prog 0 will set src mac to 00:00:00:00:00:01
+	 * if 2nd xdp prog attached on egress
+	 */
+	key = 0;
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd_0;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
+	if (ret) {
+		perror("bpf_update_elem");
+		goto out;
+	}
+
+	/* devmap prog 1 will set src mac to 00:00:00:00:01:01
+	 * if 2nd xdp prog attached on egress
+	 */
+	key = 1;
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd_1;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv8 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-19  3:12             ` [PATCHv8 " Hangbin Liu
@ 2021-01-19 14:51               ` Jesper Dangaard Brouer
  2021-01-20  4:16                 ` Hangbin Liu
  2021-01-21 13:06               ` [PATCHv9 " Hangbin Liu
  1 sibling, 1 reply; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2021-01-19 14:51 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen, brouer

On Tue, 19 Jan 2021 11:12:07 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Acked-by: Yonghong Song <yhs@fb.com>
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> 
> ---
> v8: Fix some checkpatch issues.
> 
> v7:
> a) use bpf_object__find_program_by_name() instad of
>    bpf_object__find_program_by_title()
> b) set default devmap fd to 0
> 
> v6: no code update, only rebase the code on latest bpf-next
> 
> v5:
> a) close fd when err out in get_mac_addr()
> b) exit program when both -S and -X supplied.
> 
> v4:
> a) Update get_mac_addr socket create
> b) Load dummy prog regardless of 2nd xdp prog on egress
> 
> v3:
> a) modify the src mac address based on egress mac
> 
> v2:
> a) use pkt counter instead of IP ttl modification on egress program
> b) make the egress program selectable by option -X
> ---
>  samples/bpf/xdp_redirect_map_kern.c |  79 ++++++++++++++--
>  samples/bpf/xdp_redirect_map_user.c | 140 ++++++++++++++++++++++++----
>  2 files changed, 193 insertions(+), 26 deletions(-)
> 
> diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
> index 6489352ab7a4..e5c0d07a82d8 100644
> --- a/samples/bpf/xdp_redirect_map_kern.c
> +++ b/samples/bpf/xdp_redirect_map_kern.c
> @@ -19,12 +19,22 @@
>  #include <linux/ipv6.h>
>  #include <bpf/bpf_helpers.h>
>  
> +/* The 2nd xdp prog on egress does not support skb mode, so we define two
> + * maps, tx_port_general and tx_port_native.
> + */
>  struct {
>  	__uint(type, BPF_MAP_TYPE_DEVMAP);
>  	__uint(key_size, sizeof(int));
>  	__uint(value_size, sizeof(int));
>  	__uint(max_entries, 100);
> -} tx_port SEC(".maps");
> +} tx_port_general SEC(".maps");
> +
> +struct {
> +	__uint(type, BPF_MAP_TYPE_DEVMAP);
> +	__uint(key_size, sizeof(int));
> +	__uint(value_size, sizeof(struct bpf_devmap_val));
> +	__uint(max_entries, 100);
> +} tx_port_native SEC(".maps");
>  
>  /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
>   * feedback.  Redirect TX errors can be caught via a tracepoint.
> @@ -36,6 +46,14 @@ struct {
>  	__uint(max_entries, 1);
>  } rxcnt SEC(".maps");
>  
> +/* map to store egress interface mac address */
> +struct {
> +	__uint(type, BPF_MAP_TYPE_ARRAY);
> +	__type(key, u32);
> +	__type(value, __be64);
> +	__uint(max_entries, 1);
> +} tx_mac SEC(".maps");
> +
>  static void swap_src_dst_mac(void *data)
>  {
>  	unsigned short *p = data;
> @@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
>  	p[5] = dst[2];
>  }
>  
> -SEC("xdp_redirect_map")
> -int xdp_redirect_map_prog(struct xdp_md *ctx)
> +static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
>  {
>  	void *data_end = (void *)(long)ctx->data_end;
>  	void *data = (void *)(long)ctx->data;
>  	struct ethhdr *eth = data;
>  	int rc = XDP_DROP;
> -	int vport, port = 0, m = 0;
>  	long *value;
>  	u32 key = 0;
>  	u64 nh_off;
> +	int vport;
>  
>  	nh_off = sizeof(*eth);
>  	if (data + nh_off > data_end)
> @@ -73,13 +90,63 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
>  
>  	/* count packet in global counter */
>  	value = bpf_map_lookup_elem(&rxcnt, &key);
> -	if (value)
> +	if (value) {
>  		*value += 1;
> +		if (*value % 2 == 1)
> +			vport = 1;

This will also change the base behavior of the program, e.g when we are
not testing the 2nd xdp-prog.  It will become hard to compare the
performance between xdp_redirect and xdp_redirect_map.

It looks like you are populating vport=0 and vport=1 with the same ifindex.
Thus, this code is basically doing packet reordering, due to the per
CPU bulking layer (of 16 packets) in devmap.
Is this the intended behavior?


> +	}
>  
>  	swap_src_dst_mac(data);
>  
>  	/* send packet out physical port */
> -	return bpf_redirect_map(&tx_port, vport, 0);
> +	return bpf_redirect_map(redirect_map, vport, 0);
> +}
> +
> +static int xdp_redirect_map_egress(struct xdp_md *ctx, unsigned char *mac)
> +{
> +	void *data_end = (void *)(long)ctx->data_end;
> +	void *data = (void *)(long)ctx->data;
> +	struct ethhdr *eth = data;
> +	u32 key = 0;
> +	u64 nh_off;
> +
> +	nh_off = sizeof(*eth);
> +	if (data + nh_off > data_end)
> +		return XDP_DROP;
> +
> +	__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
> +
> +	return XDP_PASS;
> +}
> +
> +SEC("xdp_redirect_general")
> +int xdp_redirect_map_general(struct xdp_md *ctx)
> +{
> +	return xdp_redirect_map(ctx, &tx_port_general);
> +}
> +
> +SEC("xdp_redirect_native")
> +int xdp_redirect_map_native(struct xdp_md *ctx)
> +{
> +	return xdp_redirect_map(ctx, &tx_port_native);
> +}
> +
> +/* This program will set src mac to 00:00:00:00:00:01 */
> +SEC("xdp_devmap/map_prog_0")
> +int xdp_redirect_map_egress_0(struct xdp_md *ctx)
> +{
> +	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
> +
> +	return xdp_redirect_map_egress(ctx, mac);
> +}
> +
> +/* This program will set src mac to 00:00:00:00:01:01 */
> +SEC("xdp_devmap/map_prog_1")
> +int xdp_redirect_map_egress_1(struct xdp_md *ctx)
> +{
> +	unsigned char mac[6] = {0x0, 0x0, 0x0, 0x0, 0x1, 0x1};
> +
> +	return xdp_redirect_map_egress(ctx, mac);
>  }
>  
>  /* Redirect require an XDP bpf_prog loaded on the TX device */
> diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
> index 31131b6e7782..99c3bdea3e49 100644
> --- a/samples/bpf/xdp_redirect_map_user.c
> +++ b/samples/bpf/xdp_redirect_map_user.c
> @@ -14,6 +14,10 @@
>  #include <unistd.h>
>  #include <libgen.h>
>  #include <sys/resource.h>
> +#include <sys/ioctl.h>
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
>  
>  #include "bpf_util.h"
>  #include <bpf/bpf.h>
> @@ -22,6 +26,7 @@
>  static int ifindex_in;
>  static int ifindex_out;
>  static bool ifindex_out_xdp_dummy_attached = true;
> +static bool xdp_devmap_attached;
>  static __u32 prog_id;
>  static __u32 dummy_prog_id;
>  
> @@ -83,6 +88,32 @@ static void poll_stats(int interval, int ifindex)
>  	}
>  }
>  
> +static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
> +{
> +	char ifname[IF_NAMESIZE];
> +	struct ifreq ifr;
> +	int fd, ret = -1;
> +
> +	fd = socket(AF_INET, SOCK_DGRAM, 0);
> +	if (fd < 0)
> +		return ret;
> +
> +	if (!if_indextoname(ifindex_out, ifname))
> +		goto err_out;
> +
> +	strcpy(ifr.ifr_name, ifname);
> +
> +	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
> +		goto err_out;
> +
> +	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
> +	ret = 0;
> +
> +err_out:
> +	close(fd);
> +	return ret;
> +}
> +
>  static void usage(const char *prog)
>  {
>  	fprintf(stderr,
> @@ -90,24 +121,27 @@ static void usage(const char *prog)
>  		"OPTS:\n"
>  		"    -S    use skb-mode\n"
>  		"    -N    enforce native mode\n"
> -		"    -F    force loading prog\n",
> +		"    -F    force loading prog\n"
> +		"    -X    load xdp program on egress\n",
>  		prog);
>  }
>  
>  int main(int argc, char **argv)
>  {
>  	struct bpf_prog_load_attr prog_load_attr = {
> -		.prog_type	= BPF_PROG_TYPE_XDP,
> +		.prog_type	= BPF_PROG_TYPE_UNSPEC,
>  	};
> -	struct bpf_program *prog, *dummy_prog;
> +	struct bpf_program *prog, *dummy_prog, *devmap_prog;
> +	int devmap_prog_fd_0 = 0, devmap_prog_fd_1 = 0;
> +	int prog_fd, dummy_prog_fd;
> +	int tx_port_map_fd, tx_mac_map_fd;
> +	struct bpf_devmap_val devmap_val;
>  	struct bpf_prog_info info = {};
>  	__u32 info_len = sizeof(info);
> -	int prog_fd, dummy_prog_fd;
> -	const char *optstr = "FSN";
> +	const char *optstr = "FSNX";
>  	struct bpf_object *obj;
>  	int ret, opt, key = 0;
>  	char filename[256];
> -	int tx_port_map_fd;
>  
>  	while ((opt = getopt(argc, argv, optstr)) != -1) {
>  		switch (opt) {
> @@ -120,14 +154,21 @@ int main(int argc, char **argv)
>  		case 'F':
>  			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
>  			break;
> +		case 'X':
> +			xdp_devmap_attached = true;
> +			break;
>  		default:
>  			usage(basename(argv[0]));
>  			return 1;
>  		}
>  	}
>  
> -	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
> +	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
>  		xdp_flags |= XDP_FLAGS_DRV_MODE;
> +	} else if (xdp_devmap_attached) {
> +		printf("Load xdp program on egress with SKB mode not supported yet\n");
> +		return 1;
> +	}
>  
>  	if (optind == argc) {
>  		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
> @@ -150,24 +191,28 @@ int main(int argc, char **argv)
>  	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
>  		return 1;
>  
> -	prog = bpf_program__next(NULL, obj);
> -	dummy_prog = bpf_program__next(prog, obj);
> -	if (!prog || !dummy_prog) {
> -		printf("finding a prog in obj file failed\n");
> -		return 1;
> +	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
> +		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
> +		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
> +	} else {
> +		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
> +		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
>  	}
> -	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
> -	 * so we're missing only the fd for dummy prog
> -	 */
> +	dummy_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_dummy_prog");
> +	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
> +		printf("finding prog/dummy_prog/tx_port_map in obj file failed\n");
> +		goto out;
> +	}
> +	prog_fd = bpf_program__fd(prog);
>  	dummy_prog_fd = bpf_program__fd(dummy_prog);
> -	if (prog_fd < 0 || dummy_prog_fd < 0) {
> +	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
>  		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
>  		return 1;
>  	}
>  
> -	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
> +	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
>  	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
> -	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
> +	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
>  		printf("bpf_object__find_map_fd_by_name failed\n");
>  		return 1;
>  	}
> @@ -199,11 +244,66 @@ int main(int argc, char **argv)
>  	}
>  	dummy_prog_id = info.id;
>  
> +	/* Load 2nd xdp prog on egress. */
> +	if (xdp_devmap_attached) {
> +		unsigned char mac_addr[6];
> +
> +		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress_0");
> +		if (!devmap_prog) {
> +			printf("finding devmap_prog in obj file failed\n");
> +			goto out;
> +		}
> +		devmap_prog_fd_0 = bpf_program__fd(devmap_prog);
> +		if (devmap_prog_fd_0 < 0) {
> +			printf("finding devmap_prog fd failed\n");
> +			goto out;
> +		}
> +
> +		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress_1");
> +		if (!devmap_prog) {
> +			printf("finding devmap_prog in obj file failed\n");
> +			goto out;
> +		}
> +		devmap_prog_fd_1 = bpf_program__fd(devmap_prog);
> +		if (devmap_prog_fd_1 < 0) {
> +			printf("finding devmap_prog fd failed\n");
> +			goto out;
> +		}
> +
> +		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
> +			printf("get interface %d mac failed\n", ifindex_out);
> +			goto out;
> +		}
> +
> +		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
> +		if (ret) {
> +			perror("bpf_update_elem tx_mac_map_fd");
> +			goto out;
> +		}
> +	}
> +
>  	signal(SIGINT, int_exit);
>  	signal(SIGTERM, int_exit);
>  
> -	/* populate virtual to physical port map */
> -	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
> +	/* devmap prog 0 will set src mac to 00:00:00:00:00:01
> +	 * if 2nd xdp prog attached on egress
> +	 */
> +	key = 0;
> +	devmap_val.ifindex = ifindex_out;

(ifindex_out same as below)

> +	devmap_val.bpf_prog.fd = devmap_prog_fd_0;
> +	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
> +	if (ret) {
> +		perror("bpf_update_elem");
> +		goto out;
> +	}
> +
> +	/* devmap prog 1 will set src mac to 00:00:00:00:01:01
> +	 * if 2nd xdp prog attached on egress
> +	 */
> +	key = 1;
> +	devmap_val.ifindex = ifindex_out;

(ifindex_out same as above)

> +	devmap_val.bpf_prog.fd = devmap_prog_fd_1;
> +	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
>  	if (ret) {
>  		perror("bpf_update_elem");
>  		goto out;



-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv8 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-19 14:51               ` Jesper Dangaard Brouer
@ 2021-01-20  4:16                 ` Hangbin Liu
  0 siblings, 0 replies; 33+ messages in thread
From: Hangbin Liu @ 2021-01-20  4:16 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen

On Tue, Jan 19, 2021 at 03:51:27PM +0100, Jesper Dangaard Brouer wrote:
> > @@ -73,13 +90,63 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
> >  
> >  	/* count packet in global counter */
> >  	value = bpf_map_lookup_elem(&rxcnt, &key);
> > -	if (value)
> > +	if (value) {
> >  		*value += 1;
> > +		if (*value % 2 == 1)
> > +			vport = 1;
> 
> This will also change the base behavior of the program, e.g when we are
> not testing the 2nd xdp-prog.  It will become hard to compare the
> performance between xdp_redirect and xdp_redirect_map.

I just did a test with/without this patch on 5.10 using pktgen

By ./xdp_redirect_map -N/-S eno1 eno1

Without this patch
- S 1.8M pps
- N 7.4M pps

With this patch
- S 1.9M pps
- N 7.4M pps

So I don't see much difference.

> 
> It looks like you are populating vport=0 and vport=1 with the same ifindex.
> Thus, this code is basically doing packet reordering, due to the per
> CPU bulking layer (of 16 packets) in devmap.
> Is this the intended behavior?

I didn't expect this could cause reordering. If we want only do it when
attached the 2nd prog, we need add a check like

key = 1;
value = bpf_map_lookup_elem(&tx_port_native , &key);
if (value)
	do_2nd_prog_test
else
	do_nothing_and_redirect_with_port_0

But an extra bpf_map_lookup_elem() for every packet may cause more performance
drop. So WDYT?

Thanks
Hangbin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv9 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-19  3:12             ` [PATCHv8 " Hangbin Liu
  2021-01-19 14:51               ` Jesper Dangaard Brouer
@ 2021-01-21 13:06               ` Hangbin Liu
  2021-01-21 15:05                 ` Jesper Dangaard Brouer
  2021-01-22  2:50                 ` [PATCHv10 " Hangbin Liu
  1 sibling, 2 replies; 33+ messages in thread
From: Hangbin Liu @ 2021-01-21 13:06 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>

---
v9:
roll back to just set src mac to egress interface mac on 2nd xdp prog,
this could avoid packet reorder introduce in patch v6.

v8: Fix some checkpatch issues.

v7:
a) use bpf_object__find_program_by_name() instad of
   bpf_object__find_program_by_title()
b) set default devmap fd to 0

v6: no code update, only rebase the code on latest bpf-next

v5:
a) close fd when err out in get_mac_addr()
b) exit program when both -S and -X supplied.

v4:
a) Update get_mac_addr socket create
b) Load dummy prog regardless of 2nd xdp prog on egress

v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  60 +++++++++++++--
 samples/bpf/xdp_redirect_map_user.c | 112 +++++++++++++++++++++++-----
 2 files changed, 147 insertions(+), 25 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..35ce5e26bbe5 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to store egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -79,7 +96,40 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+SEC("xdp_devmap/map_prog")
+int xdp_redirect_map_egress(struct xdp_md *ctx)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	__be64 *mac;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	mac = bpf_map_lookup_elem(&tx_mac, &key);
+	if (mac)
+		__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..0e8192688dfc 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -22,6 +26,7 @@
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static bool xdp_devmap_attached;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,32 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	char ifname[IF_NAMESIZE];
+	struct ifreq ifr;
+	int fd, ret = -1;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return ret;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		goto err_out;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		goto err_out;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	ret = 0;
+
+err_out:
+	close(fd);
+	return ret;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +121,26 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int prog_fd, dummy_prog_fd, devmap_prog_fd = 0;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,14 +153,21 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
 		}
 	}
 
-	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 		xdp_flags |= XDP_FLAGS_DRV_MODE;
+	} else if (xdp_devmap_attached) {
+		printf("Load xdp program on egress with SKB mode not supported yet\n");
+		return 1;
+	}
 
 	if (optind == argc) {
 		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +190,28 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
+	}
+	dummy_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_dummy_prog");
+	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/dummy_prog/tx_port_map in obj file failed\n");
+		goto out;
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
+	prog_fd = bpf_program__fd(prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -199,11 +243,39 @@ int main(int argc, char **argv)
 	}
 	dummy_prog_id = info.id;
 
+	/* Load 2nd xdp prog on egress. */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
+
+		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	}
+
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv9 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-21 13:06               ` [PATCHv9 " Hangbin Liu
@ 2021-01-21 15:05                 ` Jesper Dangaard Brouer
  2021-01-22  2:50                 ` [PATCHv10 " Hangbin Liu
  1 sibling, 0 replies; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2021-01-21 15:05 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen, brouer

On Thu, 21 Jan 2021 21:06:42 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> 
> ---
> v9:
> roll back to just set src mac to egress interface mac on 2nd xdp prog,
> this could avoid packet reorder introduce in patch v6.

I like this V9 much better.

One (hopefully) last nitpick below.

> diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
> index 6489352ab7a4..35ce5e26bbe5 100644
> --- a/samples/bpf/xdp_redirect_map_kern.c
> +++ b/samples/bpf/xdp_redirect_map_kern.c
> -SEC("xdp_redirect_map")
> -int xdp_redirect_map_prog(struct xdp_md *ctx)
> +static int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)

Can you make this function always inline?

>  {
>  	void *data_end = (void *)(long)ctx->data_end;
>  	void *data = (void *)(long)ctx->data;
>  	struct ethhdr *eth = data;
>  	int rc = XDP_DROP;
> -	int vport, port = 0, m = 0;
>  	long *value;
>  	u32 key = 0;
>  	u64 nh_off;
> +	int vport;
>  
>  	nh_off = sizeof(*eth);
>  	if (data + nh_off > data_end)
> @@ -79,7 +96,40 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
>  	swap_src_dst_mac(data);
>  
>  	/* send packet out physical port */
> -	return bpf_redirect_map(&tx_port, vport, 0);
> +	return bpf_redirect_map(redirect_map, vport, 0);
> +}
> +
> +SEC("xdp_redirect_general")
> +int xdp_redirect_map_general(struct xdp_md *ctx)
> +{
> +	return xdp_redirect_map(ctx, &tx_port_general);
> +}
> +
> +SEC("xdp_redirect_native")
> +int xdp_redirect_map_native(struct xdp_md *ctx)
> +{
> +	return xdp_redirect_map(ctx, &tx_port_native);
> +}

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCHv10 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-21 13:06               ` [PATCHv9 " Hangbin Liu
  2021-01-21 15:05                 ` Jesper Dangaard Brouer
@ 2021-01-22  2:50                 ` Hangbin Liu
  2021-01-22 10:32                   ` Jesper Dangaard Brouer
  2021-01-22 23:30                   ` patchwork-bot+netdevbpf
  1 sibling, 2 replies; 33+ messages in thread
From: Hangbin Liu @ 2021-01-22  2:50 UTC (permalink / raw)
  To: bpf
  Cc: netdev, Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
	Yonghong Song, Toke Høiland-Jørgensen, Hangbin Liu

This patch add a xdp program on egress to show that we can modify
the packet on egress. In this sample we will set the pkt's src
mac to egress's mac address. The xdp_prog will be attached when
-X option supplied.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
v10:
make xdp_redirect_map() always inline.

v9:
roll back to just set src mac to egress interface mac on 2nd xdp prog,
this could avoid packet reorder introduce in patch v6.

v8: Fix some checkpatch issues.

v7:
a) use bpf_object__find_program_by_name() instad of
   bpf_object__find_program_by_title()
b) set default devmap fd to 0

v6: no code update, only rebase the code on latest bpf-next

v5:
a) close fd when err out in get_mac_addr()
b) exit program when both -S and -X supplied.

v4:
a) Update get_mac_addr socket create
b) Load dummy prog regardless of 2nd xdp prog on egress

v3:
a) modify the src mac address based on egress mac

v2:
a) use pkt counter instead of IP ttl modification on egress program
b) make the egress program selectable by option -X
---
 samples/bpf/xdp_redirect_map_kern.c |  60 +++++++++++++--
 samples/bpf/xdp_redirect_map_user.c | 112 +++++++++++++++++++++++-----
 2 files changed, 147 insertions(+), 25 deletions(-)

diff --git a/samples/bpf/xdp_redirect_map_kern.c b/samples/bpf/xdp_redirect_map_kern.c
index 6489352ab7a4..a92b8e567bdd 100644
--- a/samples/bpf/xdp_redirect_map_kern.c
+++ b/samples/bpf/xdp_redirect_map_kern.c
@@ -19,12 +19,22 @@
 #include <linux/ipv6.h>
 #include <bpf/bpf_helpers.h>
 
+/* The 2nd xdp prog on egress does not support skb mode, so we define two
+ * maps, tx_port_general and tx_port_native.
+ */
 struct {
 	__uint(type, BPF_MAP_TYPE_DEVMAP);
 	__uint(key_size, sizeof(int));
 	__uint(value_size, sizeof(int));
 	__uint(max_entries, 100);
-} tx_port SEC(".maps");
+} tx_port_general SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_DEVMAP);
+	__uint(key_size, sizeof(int));
+	__uint(value_size, sizeof(struct bpf_devmap_val));
+	__uint(max_entries, 100);
+} tx_port_native SEC(".maps");
 
 /* Count RX packets, as XDP bpf_prog doesn't get direct TX-success
  * feedback.  Redirect TX errors can be caught via a tracepoint.
@@ -36,6 +46,14 @@ struct {
 	__uint(max_entries, 1);
 } rxcnt SEC(".maps");
 
+/* map to store egress interface mac address */
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__type(key, u32);
+	__type(value, __be64);
+	__uint(max_entries, 1);
+} tx_mac SEC(".maps");
+
 static void swap_src_dst_mac(void *data)
 {
 	unsigned short *p = data;
@@ -52,17 +70,16 @@ static void swap_src_dst_mac(void *data)
 	p[5] = dst[2];
 }
 
-SEC("xdp_redirect_map")
-int xdp_redirect_map_prog(struct xdp_md *ctx)
+static __always_inline int xdp_redirect_map(struct xdp_md *ctx, void *redirect_map)
 {
 	void *data_end = (void *)(long)ctx->data_end;
 	void *data = (void *)(long)ctx->data;
 	struct ethhdr *eth = data;
 	int rc = XDP_DROP;
-	int vport, port = 0, m = 0;
 	long *value;
 	u32 key = 0;
 	u64 nh_off;
+	int vport;
 
 	nh_off = sizeof(*eth);
 	if (data + nh_off > data_end)
@@ -79,7 +96,40 @@ int xdp_redirect_map_prog(struct xdp_md *ctx)
 	swap_src_dst_mac(data);
 
 	/* send packet out physical port */
-	return bpf_redirect_map(&tx_port, vport, 0);
+	return bpf_redirect_map(redirect_map, vport, 0);
+}
+
+SEC("xdp_redirect_general")
+int xdp_redirect_map_general(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_general);
+}
+
+SEC("xdp_redirect_native")
+int xdp_redirect_map_native(struct xdp_md *ctx)
+{
+	return xdp_redirect_map(ctx, &tx_port_native);
+}
+
+SEC("xdp_devmap/map_prog")
+int xdp_redirect_map_egress(struct xdp_md *ctx)
+{
+	void *data_end = (void *)(long)ctx->data_end;
+	void *data = (void *)(long)ctx->data;
+	struct ethhdr *eth = data;
+	__be64 *mac;
+	u32 key = 0;
+	u64 nh_off;
+
+	nh_off = sizeof(*eth);
+	if (data + nh_off > data_end)
+		return XDP_DROP;
+
+	mac = bpf_map_lookup_elem(&tx_mac, &key);
+	if (mac)
+		__builtin_memcpy(eth->h_source, mac, ETH_ALEN);
+
+	return XDP_PASS;
 }
 
 /* Redirect require an XDP bpf_prog loaded on the TX device */
diff --git a/samples/bpf/xdp_redirect_map_user.c b/samples/bpf/xdp_redirect_map_user.c
index 31131b6e7782..0e8192688dfc 100644
--- a/samples/bpf/xdp_redirect_map_user.c
+++ b/samples/bpf/xdp_redirect_map_user.c
@@ -14,6 +14,10 @@
 #include <unistd.h>
 #include <libgen.h>
 #include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
 
 #include "bpf_util.h"
 #include <bpf/bpf.h>
@@ -22,6 +26,7 @@
 static int ifindex_in;
 static int ifindex_out;
 static bool ifindex_out_xdp_dummy_attached = true;
+static bool xdp_devmap_attached;
 static __u32 prog_id;
 static __u32 dummy_prog_id;
 
@@ -83,6 +88,32 @@ static void poll_stats(int interval, int ifindex)
 	}
 }
 
+static int get_mac_addr(unsigned int ifindex_out, void *mac_addr)
+{
+	char ifname[IF_NAMESIZE];
+	struct ifreq ifr;
+	int fd, ret = -1;
+
+	fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (fd < 0)
+		return ret;
+
+	if (!if_indextoname(ifindex_out, ifname))
+		goto err_out;
+
+	strcpy(ifr.ifr_name, ifname);
+
+	if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
+		goto err_out;
+
+	memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
+	ret = 0;
+
+err_out:
+	close(fd);
+	return ret;
+}
+
 static void usage(const char *prog)
 {
 	fprintf(stderr,
@@ -90,24 +121,26 @@ static void usage(const char *prog)
 		"OPTS:\n"
 		"    -S    use skb-mode\n"
 		"    -N    enforce native mode\n"
-		"    -F    force loading prog\n",
+		"    -F    force loading prog\n"
+		"    -X    load xdp program on egress\n",
 		prog);
 }
 
 int main(int argc, char **argv)
 {
 	struct bpf_prog_load_attr prog_load_attr = {
-		.prog_type	= BPF_PROG_TYPE_XDP,
+		.prog_type	= BPF_PROG_TYPE_UNSPEC,
 	};
-	struct bpf_program *prog, *dummy_prog;
+	struct bpf_program *prog, *dummy_prog, *devmap_prog;
+	int prog_fd, dummy_prog_fd, devmap_prog_fd = 0;
+	int tx_port_map_fd, tx_mac_map_fd;
+	struct bpf_devmap_val devmap_val;
 	struct bpf_prog_info info = {};
 	__u32 info_len = sizeof(info);
-	int prog_fd, dummy_prog_fd;
-	const char *optstr = "FSN";
+	const char *optstr = "FSNX";
 	struct bpf_object *obj;
 	int ret, opt, key = 0;
 	char filename[256];
-	int tx_port_map_fd;
 
 	while ((opt = getopt(argc, argv, optstr)) != -1) {
 		switch (opt) {
@@ -120,14 +153,21 @@ int main(int argc, char **argv)
 		case 'F':
 			xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
 			break;
+		case 'X':
+			xdp_devmap_attached = true;
+			break;
 		default:
 			usage(basename(argv[0]));
 			return 1;
 		}
 	}
 
-	if (!(xdp_flags & XDP_FLAGS_SKB_MODE))
+	if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
 		xdp_flags |= XDP_FLAGS_DRV_MODE;
+	} else if (xdp_devmap_attached) {
+		printf("Load xdp program on egress with SKB mode not supported yet\n");
+		return 1;
+	}
 
 	if (optind == argc) {
 		printf("usage: %s <IFNAME|IFINDEX>_IN <IFNAME|IFINDEX>_OUT\n", argv[0]);
@@ -150,24 +190,28 @@ int main(int argc, char **argv)
 	if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
 		return 1;
 
-	prog = bpf_program__next(NULL, obj);
-	dummy_prog = bpf_program__next(prog, obj);
-	if (!prog || !dummy_prog) {
-		printf("finding a prog in obj file failed\n");
-		return 1;
+	if (xdp_flags & XDP_FLAGS_SKB_MODE) {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_general");
+	} else {
+		prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native");
+		tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port_native");
+	}
+	dummy_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_dummy_prog");
+	if (!prog || dummy_prog < 0 || tx_port_map_fd < 0) {
+		printf("finding prog/dummy_prog/tx_port_map in obj file failed\n");
+		goto out;
 	}
-	/* bpf_prog_load_xattr gives us the pointer to first prog's fd,
-	 * so we're missing only the fd for dummy prog
-	 */
+	prog_fd = bpf_program__fd(prog);
 	dummy_prog_fd = bpf_program__fd(dummy_prog);
-	if (prog_fd < 0 || dummy_prog_fd < 0) {
+	if (prog_fd < 0 || dummy_prog_fd < 0 || tx_port_map_fd < 0) {
 		printf("bpf_prog_load_xattr: %s\n", strerror(errno));
 		return 1;
 	}
 
-	tx_port_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_port");
+	tx_mac_map_fd = bpf_object__find_map_fd_by_name(obj, "tx_mac");
 	rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt");
-	if (tx_port_map_fd < 0 || rxcnt_map_fd < 0) {
+	if (tx_mac_map_fd < 0 || rxcnt_map_fd < 0) {
 		printf("bpf_object__find_map_fd_by_name failed\n");
 		return 1;
 	}
@@ -199,11 +243,39 @@ int main(int argc, char **argv)
 	}
 	dummy_prog_id = info.id;
 
+	/* Load 2nd xdp prog on egress. */
+	if (xdp_devmap_attached) {
+		unsigned char mac_addr[6];
+
+		devmap_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_egress");
+		if (!devmap_prog) {
+			printf("finding devmap_prog in obj file failed\n");
+			goto out;
+		}
+		devmap_prog_fd = bpf_program__fd(devmap_prog);
+		if (devmap_prog_fd < 0) {
+			printf("finding devmap_prog fd failed\n");
+			goto out;
+		}
+
+		if (get_mac_addr(ifindex_out, mac_addr) < 0) {
+			printf("get interface %d mac failed\n", ifindex_out);
+			goto out;
+		}
+
+		ret = bpf_map_update_elem(tx_mac_map_fd, &key, mac_addr, 0);
+		if (ret) {
+			perror("bpf_update_elem tx_mac_map_fd");
+			goto out;
+		}
+	}
+
 	signal(SIGINT, int_exit);
 	signal(SIGTERM, int_exit);
 
-	/* populate virtual to physical port map */
-	ret = bpf_map_update_elem(tx_port_map_fd, &key, &ifindex_out, 0);
+	devmap_val.ifindex = ifindex_out;
+	devmap_val.bpf_prog.fd = devmap_prog_fd;
+	ret = bpf_map_update_elem(tx_port_map_fd, &key, &devmap_val, 0);
 	if (ret) {
 		perror("bpf_update_elem");
 		goto out;
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCHv10 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-22  2:50                 ` [PATCHv10 " Hangbin Liu
@ 2021-01-22 10:32                   ` Jesper Dangaard Brouer
  2021-01-22 23:30                   ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 33+ messages in thread
From: Jesper Dangaard Brouer @ 2021-01-22 10:32 UTC (permalink / raw)
  To: Hangbin Liu
  Cc: bpf, netdev, Daniel Borkmann, John Fastabend, Yonghong Song,
	Toke Høiland-Jørgensen, brouer

On Fri, 22 Jan 2021 10:50:07 +0800
Hangbin Liu <liuhangbin@gmail.com> wrote:

> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> ---

I think we have nitpicked this enough ;-)

Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>

> v10:
> make xdp_redirect_map() always inline.
> 
> v9:
> roll back to just set src mac to egress interface mac on 2nd xdp prog,
> this could avoid packet reorder introduce in patch v6.
> 
> v8: Fix some checkpatch issues.
> 
> v7:
> a) use bpf_object__find_program_by_name() instad of
>    bpf_object__find_program_by_title()
> b) set default devmap fd to 0
> 
> v6: no code update, only rebase the code on latest bpf-next
> 
> v5:
> a) close fd when err out in get_mac_addr()
> b) exit program when both -S and -X supplied.
> 
> v4:
> a) Update get_mac_addr socket create
> b) Load dummy prog regardless of 2nd xdp prog on egress
> 
> v3:
> a) modify the src mac address based on egress mac
> 
> v2:
> a) use pkt counter instead of IP ttl modification on egress program
> b) make the egress program selectable by option -X
> ---
>  samples/bpf/xdp_redirect_map_kern.c |  60 +++++++++++++--
>  samples/bpf/xdp_redirect_map_user.c | 112 +++++++++++++++++++++++-----
>  2 files changed, 147 insertions(+), 25 deletions(-)
 
-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  LinkedIn: http://www.linkedin.com/in/brouer


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCHv10 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
  2021-01-22  2:50                 ` [PATCHv10 " Hangbin Liu
  2021-01-22 10:32                   ` Jesper Dangaard Brouer
@ 2021-01-22 23:30                   ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 33+ messages in thread
From: patchwork-bot+netdevbpf @ 2021-01-22 23:30 UTC (permalink / raw)
  To: Hangbin Liu; +Cc: bpf, netdev, daniel, brouer, john.fastabend, yhs, toke

Hello:

This patch was applied to bpf/bpf-next.git (refs/heads/master):

On Fri, 22 Jan 2021 10:50:07 +0800 you wrote:
> This patch add a xdp program on egress to show that we can modify
> the packet on egress. In this sample we will set the pkt's src
> mac to egress's mac address. The xdp_prog will be attached when
> -X option supplied.
> 
> Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
> 
> [...]

Here is the summary with links:
  - [PATCHv10,bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map
    https://git.kernel.org/bpf/bpf-next/c/6e66fbb10597

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2021-01-22 23:31 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-10 12:46 [PATCH bpf-next] samples/bpf: add xdp_redirect_map with xdp_prog support Hangbin Liu
2020-11-10 14:25 ` Jesper Dangaard Brouer
2020-11-10 15:24   ` Maciej Fijalkowski
2020-11-11  1:12   ` Hangbin Liu
2020-11-26  8:43 ` [PATCHv2 bpf-next] samples/bpf: add xdp program on egress for xdp_redirect_map Hangbin Liu
2020-11-26 10:51   ` Jesper Dangaard Brouer
2020-11-26 14:19     ` Hangbin Liu
2020-11-27  6:31   ` Yonghong Song
2020-11-30  7:51     ` Hangbin Liu
2020-11-30  9:32       ` Jesper Dangaard Brouer
2020-11-30 13:10         ` Hangbin Liu
2020-11-30 15:12           ` Jesper Dangaard Brouer
2020-11-30 16:07             ` Toke Høiland-Jørgensen
2020-12-08  8:18   ` [PATCHv3 " Hangbin Liu
2020-12-08 10:39     ` Jesper Dangaard Brouer
2020-12-08 11:11       ` Hangbin Liu
2020-12-08 12:01     ` [PATCHv4 " Hangbin Liu
2020-12-11  0:15       ` Daniel Borkmann
2020-12-11  2:40       ` [PATCHv5 " Hangbin Liu
2021-01-14 14:27         ` [PATCHv6 " Hangbin Liu
2021-01-14 21:01           ` Yonghong Song
2021-01-15  4:17             ` Hangbin Liu
2021-01-15  6:24           ` [PATCHv7 " Hangbin Liu
2021-01-15 16:57             ` Yonghong Song
2021-01-18 22:46             ` Daniel Borkmann
2021-01-19  3:12             ` [PATCHv8 " Hangbin Liu
2021-01-19 14:51               ` Jesper Dangaard Brouer
2021-01-20  4:16                 ` Hangbin Liu
2021-01-21 13:06               ` [PATCHv9 " Hangbin Liu
2021-01-21 15:05                 ` Jesper Dangaard Brouer
2021-01-22  2:50                 ` [PATCHv10 " Hangbin Liu
2021-01-22 10:32                   ` Jesper Dangaard Brouer
2021-01-22 23:30                   ` patchwork-bot+netdevbpf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.