From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751269AbdJCHmF (ORCPT ); Tue, 3 Oct 2017 03:42:05 -0400 Received: from mail-sn1nam01on0063.outbound.protection.outlook.com ([104.47.32.63]:52011 "EHLO NAM01-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1750720AbdJCHmB (ORCPT ); Tue, 3 Oct 2017 03:42:01 -0400 Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=Christina.Jacob@cavium.com; From: cjacob To: netdev@vger.kernel.org Cc: Christina.Jacob@cavium.com, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH 1/1] xdp: Sample xdp program implementing ip forward Date: Tue, 3 Oct 2017 13:07:05 +0530 Message-Id: <1507016225-319-2-git-send-email-Christina.Jacob@cavium.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1507016225-319-1-git-send-email-Christina.Jacob@cavium.com> References: <1507016225-319-1-git-send-email-Christina.Jacob@cavium.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [14.140.2.178] X-ClientProxiedBy: BM1PR01CA0077.INDPRD01.PROD.OUTLOOK.COM (10.174.208.145) To DM5PR07MB3467.namprd07.prod.outlook.com (10.164.153.22) X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: fcedf0e9-e561-4fd0-35ab-08d50a323ac8 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(22001)(2017030254152)(2017052603199)(201703131423075)(201703031133081)(201702281549075);SRVR:DM5PR07MB3467; X-Microsoft-Exchange-Diagnostics: 1;DM5PR07MB3467;3:Xp377Tiz2WDO1Y3OgWCAEHVnHZYpkL5PTO/VZGVYBA6DibM6flAHh7SDYrMP9fbJAdAE1VhHvUu2lZXolBRyf4kqpeAebv/Yp16PP4WpZjXCi1JIZK6kUtTHgr/susg2yLL2veZQ7TIzV1Z2FCA4MsGv0WBxThuF3wNpZt2PzddI+mLLsTLIR3MIXM/sX4DE6I+aPxU/BiDCn93JhTCvhfk4oD4EsOC4nlGkcZKzyBeNON3q/sBj9vfv+hVrSjoE;25:Af7yGrZU2MtGzZSPAf3t6/Iss7pGCM1S9ZQggSQmR1ateioNWbqDX0kDty8s8iPDC9D2HQq299fP7VIBc08BkvDoiHtvrXqKf3M75cIJx+06rlCGYQoOfWgqopU9dt/zNcH+bfHTMPTJ9TY0v1xqKDmnpNMPKx+wt/BkZ2vemnjwdT1tRPeWuXO4ONBpXnOqfH1DHmNo3b7kBK2Ed/GGikUgzlpzl6Mkm+6XY7vQn3410XtRGIH6E30WhcNp/2MJpiPkZxIBXf5cu+5VlwvzyUogmfYpTm7bSEzZCsSPdrssQeFHOFiOFlhAt3eOCsdlx2hrd8g3wxBX4b0zpubR/Q==;31:WheRw6Ak6VXG58JQMR1big5juyky9U/6CfVw4e5/gnIRinQboKxgSu+7NG1au8EQjbbrYCQq8dr+svuKDweooHzS162DklB5+B5A8zBh8/tqCci5Yc4ipcCVXZHQmy23qRYqR+lUz8tkgwlBYaFomjAT+ZsG3gc5NYOPImqK921yC27QVkxMXYwzcuOUeDtm6gDDavM2wGGuGZDEkB+BoZaszgQfQBrNdZdDZbrX0Ng= X-MS-TrafficTypeDiagnostic: DM5PR07MB3467: X-Microsoft-Exchange-Diagnostics: 1;DM5PR07MB3467;20:Yp1QevzByQqISAIYhYC+dlyeG3wce+IOJdEG59azTi+HUg3U9/u1WC7u8LDcVRvzw13nUL+AkXEqeE4FRpyH8Ak4lBV1rBpzpRhIG9oSAAkElUthzQLX7TgA6JKm1xJ4QA+5+20RhYAJq7OMdEp8OIsDknrDRyAjj79seRcaN8nyG8unv8YDZAlszQMwjyJqyE9anUaKjGMn7Cl2tqB2hK0LSzRbD+d9LXKXKVFKkRL595ra8MQnAWIQ4YtH3gVG48uagARICXrGApVmIbyDoT0t9kNWuF07jXrNRqmimuTtXqO1yEkmcBIJRQg62BIw+RmJeCAC8MRPua7ZDW/7FYPMkHwQFLt0KaU8UWGLt1z898DmxASTh7nfol6Q76ujDEabmOoVPXXQnJmk+7Pgg1ZAjnwVJ63Pc/uGUk7/KqTyNcjJuBHAvRLUUf3c469YmuT6pykaGJcS+cfwxUAegZ2AZjLCnnVF/tOhXgyEK4XvApJpILkVtlG6IBxuZULC;4:7bHF+rCE5pDoRpxDgXYT/9xyXIW8g1CjRr2dxJixkfURzFSr5lNMeJkTaEZRYmfU1dxHbvvRf/jpFw5QKIIz5XXoD4sea1uuf1A9WHc8FNLnAuYkU2YMerfEPr+yMOUDC9She0HgHk79/9F8Ed+bWB7BSZM9ScMg8/qC9fQjvAmRe+HKoIkyMy0fCkwgmyo/GujR4AuM7S9QL9Uo88+H2xWVr/+AjLEyj1glKgZr3kfI9lj6sZuiaYvqpROO9FES X-Exchange-Antispam-Report-Test: UriScan:; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(100000700101)(100105000095)(100000701101)(100105300095)(100000702101)(100105100095)(6040450)(2401047)(8121501046)(5005006)(93006095)(93001095)(10201501046)(3002001)(100000703101)(100105400095)(6041248)(20161123564025)(20161123560025)(20161123558100)(201703131423075)(201702281528075)(201703061421075)(201703061406153)(20161123562025)(20161123555025)(6072148)(201708071742011)(100000704101)(100105200095)(100000705101)(100105500095);SRVR:DM5PR07MB3467;BCL:0;PCL:0;RULEID:(100000800101)(100110000095)(100000801101)(100110300095)(100000802101)(100110100095)(100000803101)(100110400095)(100000804101)(100110200095)(100000805101)(100110500095);SRVR:DM5PR07MB3467; X-Forefront-PRVS: 044968D9E1 X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10009020)(6069001)(6009001)(346002)(376002)(189002)(199003)(6916009)(25786009)(105586002)(2351001)(106356001)(81156014)(7736002)(81166006)(8676002)(50466002)(86362001)(575784001)(8936002)(50226002)(2906002)(3846002)(48376002)(6116002)(86152003)(305945005)(2361001)(2950100002)(316002)(16586007)(36756003)(97736004)(189998001)(53936002)(6512007)(5009440100003)(5660300001)(47776003)(4326008)(66066001)(16526017)(76176999)(50986999)(5890100001)(72206003)(68736007)(101416001)(6666003)(6486002)(6506006)(478600001)(5003940100001)(110426004)(43062003)(2004002);DIR:OUT;SFP:1101;SCL:1;SRVR:DM5PR07MB3467;H:localhost.localdomain;FPR:;SPF:None;PTR:InfoNoRecords;MX:1;A:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;DM5PR07MB3467;23:Eqw5Qfx7/MH9p7AXrAlMhc2hqALUI+s3TNHVUzOTC?= =?us-ascii?Q?PTcXK18h7qR2tO+xjcWE+p4nep4ofCV07VWqaSXls+WPJPiSwUikaU04YKyx?= =?us-ascii?Q?ecJc2BIacPptIIHj0VwCKtFtIuYw/yoDc3ZBQg7GvMFWYKcLK4mP+Hz/61RS?= =?us-ascii?Q?vjMb2ZjZUfFHWH60nU480BYRKJirKdEBiMg434Kk1NvaI+494YQs7Qj+xZAm?= =?us-ascii?Q?mGICD3nwweTg+3kWSnk7TJsbGitRPedmRG9QYpiDhG2e74TJZ+bIDqk87fwj?= =?us-ascii?Q?DMbNJiIiKcIM0re1WUAJVxUznU0bD2yRP/LDnqaWoKV/C9ewpGxAzsvhTvQ9?= =?us-ascii?Q?xk37uQ/NK0+eRNZfkVkw+JSR4qi43Fs0de96cLMOoU3srrkPQGN8VybP1Gju?= =?us-ascii?Q?UiIFA3HsaSJAJ4MfucCh57hVMg9VF6DYSbFF02b9eWfWWax6B/xC/Tncq82u?= =?us-ascii?Q?1GBRICaAwWLBNE+LRQZCRJtPCifnwZ/HTScYEpigPyugzOR/r1Ifl/q/M/QG?= =?us-ascii?Q?uf1blfn7ey496t54oZoMHTWaFKfms67hbAyYTTwZz+ydsfTV6VMDk6wj3HUt?= =?us-ascii?Q?bPNHB+RVRIgMbT7EQG2Ce/iZQIbYzoUuAmuBpiSnXXf+qhxcAQSbaqOroGv4?= =?us-ascii?Q?HcZ0DybrSa2JfPAO95yJQDPhGZ4t0Z34EGEOUsQofTj/uIOlwVLYd3jryHZY?= =?us-ascii?Q?SEAlAL96q9kyXMV+G1w6ytTcxydi6uEB4KLoLkYpaIXoCvCcfIphWkAapMmd?= =?us-ascii?Q?6N0ZZAMtFRV3TCgib2D/Lewo4/UGsJ/dsWS5+W/2rwLurtHrdWDXE/TaCQa6?= =?us-ascii?Q?gVijGznKbkoKSKshCunAOGAvR8+kuEp1Wx+stfIld39xVLQknIpz/pav1bLi?= =?us-ascii?Q?q6jPmxCBX+WPeh6LZwm3rybWW7CXIS36faCfnsEaBRgjhw2dksg+xNMwylVq?= =?us-ascii?Q?w+vkHlC0JWZj2L8x7qXuqbXCoo/dkZzOOBE7T6Pp2zRK8Ssk43m2tuSq5R26?= =?us-ascii?Q?r2Vfk/qDAXuogItPCYKzwJfM22hREbInn2aPU1I3e7f58hFYf/sItQwGrUlU?= =?us-ascii?Q?3qjs8sA/KpajrA6zb1vdkexIw+AxB8WiWQ+s1TL+q9No3UnCt+WdMDw34e0c?= =?us-ascii?Q?R+C4WZxxmvsma+bALRqL+k7Glm2Q8vZCUD/xNmfm69yP4ahNqw5IuTgYAx1w?= =?us-ascii?Q?MGcS8TNJnCFUVaegXp9M4h7UIbnF63IU8egWnsg2HL7sKlkwegU0Zoul9xXf?= =?us-ascii?Q?uf3AU5ocv9kBhFR4c9YslZQvPmU6wdxXBkxXOPMy3uqDimfNR4bhGBOUGta1?= =?us-ascii?B?Zz09?= X-Microsoft-Exchange-Diagnostics: 1;DM5PR07MB3467;6:NwQV67diKYVXDQMYGZZjBEoVYwU0WmmdnYho/MVAjjY/vpjjaIiJ2BVyrGpx+FNQzFlOQPW50H/+dtsBJ7a+BVhU64dq3xPsNUViPksx2npCle8ZCFMj7udS1TSn4Zno0PLd8xUJCA06sFxphOQOxn9foHG8FRHyqZM/qb7Dw6xJx8ypptwBihgKjSi7zAzjOfYFWwy7HKMOJXrDZcDH5QxbwLDlvjFKvwfvM1ReDgbsVgAm8Y36A1u2WYIoukgRGDOn5pYecBLDrQi0qOmKnp8iR+b/W5EunqNWYgsxvOKInT2wD+mJVBtoWdrOihxTApaHLqEbtlmlf8c/lt0XOQ==;5:WFgHJo5fXICtlxUDlhr78mGMdsIeeJxgMTLHMaVAesszDmLFx3OTOSNS9rfpzHec55k2ZUU+yKz673lh6vbsHFFTkobz9oWgjcnf57pVrTuXcb7d7bGK7noEAZH/Ypku3605tkVw3rC9/kDY4umVBaUnuG3NEZbSv4+h5D3nCJw=;24:+DufNiMyise0q3fG1vD0U9Mz6Cra+eC/DXO4jH95T3ZladG342UWxHshdWhxHv/m94uO14tmf5BcJhiIUKUgiKfm/6HDKJ0mQenueITXXb8=;7:eagL0hsKQwuv0Lh5mi5bXoP5KidAoCH5+207vDiCzckX2JhoFRRmlnpPtmc/xRAUDoi2n5d39HTn6emWvkm6ZHEJMK5O2awCfMDcbHY3phLRRO9G2+pkLQKEcWwxXdIbtJXJXJnZVExvOtOBvooGmPfuHona+ngsPWgB7TFPbOfL6ntfmWYi6UT2Zo0lgdprDodXVmqGD3JbOYot0/lViKxoQGWo7ha0YcQ4G8uPPlE= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: cavium.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Oct 2017 07:41:57.6703 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 711e4ccf-2e9b-4bcf-a551-4094005b6194 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM5PR07MB3467 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implements port to port forwarding with route table and arp table lookup for ipv4 packets using bpf_redirect helper function and lpm_trie map. Signed-off-by: cjacob --- samples/bpf/Makefile | 4 + samples/bpf/xdp3_kern.c | 204 +++++++++++++++ samples/bpf/xdp3_user.c | 649 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 857 insertions(+), 0 deletions(-) diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index cf17c79..cc9cc0b 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -28,6 +28,7 @@ hostprogs-y += test_cgrp2_sock hostprogs-y += test_cgrp2_sock2 hostprogs-y += xdp1 hostprogs-y += xdp2 +hostprogs-y += xdp3 hostprogs-y += test_current_task_under_cgroup hostprogs-y += trace_event hostprogs-y += sampleip @@ -73,6 +74,7 @@ test_cgrp2_sock2-objs := bpf_load.o $(LIBBPF) test_cgrp2_sock2.o xdp1-objs := bpf_load.o $(LIBBPF) xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := bpf_load.o $(LIBBPF) xdp1_user.o +xdp3-objs := bpf_load.o $(LIBBPF) xdp3_user.o test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) cgroup_helpers.o \ test_current_task_under_cgroup_user.o trace_event-objs := bpf_load.o $(LIBBPF) trace_event_user.o @@ -114,6 +116,7 @@ always += parse_varlen.o parse_simple.o parse_ldabs.o always += test_cgrp2_tc_kern.o always += xdp1_kern.o always += xdp2_kern.o +always += xdp3_kern.o always += test_current_task_under_cgroup_kern.o always += trace_event_kern.o always += sampleip_kern.o @@ -160,6 +163,7 @@ HOSTLOADLIBES_map_perf_test += -lelf -lrt HOSTLOADLIBES_test_overhead += -lelf -lrt HOSTLOADLIBES_xdp1 += -lelf HOSTLOADLIBES_xdp2 += -lelf +HOSTLOADLIBES_xdp3 += -lelf HOSTLOADLIBES_test_current_task_under_cgroup += -lelf HOSTLOADLIBES_trace_event += -lelf HOSTLOADLIBES_sampleip += -lelf diff --git a/samples/bpf/xdp3_kern.c b/samples/bpf/xdp3_kern.c new file mode 100644 index 0000000..62d905d --- /dev/null +++ b/samples/bpf/xdp3_kern.c @@ -0,0 +1,204 @@ +/* Copyright (c) 2016 PLUMgrid + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#define KBUILD_MODNAME "foo" +#include +#include +#include +#include +#include +#include +#include +#include "bpf_helpers.h" +#include +#include + +struct trie_value { + __u8 prefix[4]; + long value; + int gw; + int ifindex; + int metric; +}; + +union key_4 { + u32 b32[2]; + u8 b8[8]; +}; + +struct arp_entry { + int dst; + long mac; +}; + +struct direct_map { + long mac; + int ifindex; + struct arp_entry arp; +}; + +/* Map for trie implementation*/ +struct bpf_map_def SEC("maps") lpm_map = { + .type = BPF_MAP_TYPE_LPM_TRIE, + .key_size = 8, + .value_size = + sizeof(struct trie_value), + .max_entries = 50, + .map_flags = BPF_F_NO_PREALLOC, +}; + +/* Map for counter*/ +struct bpf_map_def SEC("maps") rxcnt = { + .type = BPF_MAP_TYPE_PERCPU_ARRAY, + .key_size = sizeof(u32), + .value_size = sizeof(long), + .max_entries = 256, +}; + +/* Map for ARP table*/ +struct bpf_map_def SEC("maps") arp_table = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(int), + .value_size = sizeof(long), + .max_entries = 50, +}; + +/* Map to keep the exact match entries in the route table*/ +struct bpf_map_def SEC("maps") exact_match = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(int), + .value_size = sizeof(struct direct_map), + .max_entries = 50, +}; + +/** + * Function to set source and destination mac of the packet + */ +static inline void set_src_dst_mac(void *data, void *src, void *dst) +{ + unsigned short *p = data; + unsigned short *dest = dst; + unsigned short *source = src; + + p[3] = source[0]; + p[4] = source[1]; + p[5] = source[2]; + p[0] = dest[0]; + p[1] = dest[1]; + p[2] = dest[2]; +} + +/** + * Parse IPV4 packet to get SRC, DST IP and protocol + */ +static inline int parse_ipv4(void *data, u64 nh_off, void *data_end, + unsigned int *src, unsigned int *dest) +{ + struct iphdr *iph = data + nh_off; + + if (iph + 1 > data_end) + return 0; + *src = (unsigned int)iph->saddr; + *dest = (unsigned int)iph->daddr; + return iph->protocol; +} + +SEC("xdp3") +int xdp_prog3(struct xdp_md *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct ethhdr *eth = data; + int rc = XDP_DROP, forward_to; + long *value; + struct trie_value *prefix_value; + long *dest_mac = NULL, *src_mac = NULL; + u16 h_proto; + u64 nh_off; + u32 ipproto; + union key_4 key4; + + nh_off = sizeof(*eth); + if (data + nh_off > data_end) + return rc; + + h_proto = eth->h_proto; + + if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { + struct vlan_hdr *vhdr; + + vhdr = data + nh_off; + nh_off += sizeof(struct vlan_hdr); + if (data + nh_off > data_end) + return rc; + h_proto = vhdr->h_vlan_encapsulated_proto; + } + if (h_proto == htons(ETH_P_ARP)) { + return XDP_PASS; + } else if (h_proto == htons(ETH_P_IP)) { + int src_ip = 0, dest_ip = 0; + struct direct_map *direct_entry; + + ipproto = parse_ipv4(data, nh_off, data_end, &src_ip, &dest_ip); + direct_entry = (struct direct_map *)bpf_map_lookup_elem + (&exact_match, &dest_ip); + /*check for exact match, this would give a faster lookup*/ + if (direct_entry && direct_entry->mac && + direct_entry->arp.mac) { + src_mac = &direct_entry->mac; + dest_mac = &direct_entry->arp.mac; + forward_to = direct_entry->ifindex; + } else { + /*Look up in the trie for lpm*/ + // Key for trie + key4.b32[0] = 32; + key4.b8[4] = dest_ip % 0x100; + key4.b8[5] = (dest_ip >> 8) % 0x100; + key4.b8[6] = (dest_ip >> 16) % 0x100; + key4.b8[7] = (dest_ip >> 24) % 0x100; + prefix_value = + ((struct trie_value *)bpf_map_lookup_elem + (&lpm_map, &key4)); + if (!prefix_value) { + return XDP_DROP; + } else { + src_mac = &prefix_value->value; + if (src_mac) { + dest_mac = (long *)bpf_map_lookup_elem + (&arp_table, &dest_ip); + if (!dest_mac) { + if (prefix_value->gw) { + dest_ip = *(unsigned int *)(&(prefix_value->gw)); + dest_mac = (long *)bpf_map_lookup_elem + (&arp_table, &dest_ip); + } else { + return XDP_DROP; + } + } + forward_to = prefix_value->ifindex; + } else { + return XDP_DROP; + } + } + } + } else { + ipproto = 0; + } + if (src_mac && dest_mac) { + set_src_dst_mac(data, src_mac, + dest_mac); + value = bpf_map_lookup_elem + (&rxcnt, &ipproto); + if (value) + *value += 1; + return bpf_redirect( + forward_to, + 0); + } + return rc; +} + +char _license[] SEC("license") = "GPL"; diff --git a/samples/bpf/xdp3_user.c b/samples/bpf/xdp3_user.c new file mode 100644 index 0000000..451b522 --- /dev/null +++ b/samples/bpf/xdp3_user.c @@ -0,0 +1,649 @@ +/* Copyright (c) 2016 PLUMgrid + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_load.h" +#include "libbpf.h" +#include +#include +#include +#include +#include +#include +#include "bpf_util.h" +#include + +int sock, sock_arp, flags = 0; +char buf[8192]; +static int total_ifindex; +char **index_list; + +static int get_route_table(int rtm_family); +static void int_exit(int sig) +{ + int i = 0, index; + + for (i = 0; i < total_ifindex; i++) { + index = strtoul(index_list[i], NULL, 0); + set_link_xdp_fd(index, -1, flags); + } + exit(0); +} + +static void close_and_exit(int sig) +{ + int i = 0, index; + + close(sock); + close(sock_arp); + + for (i = 0; i < total_ifindex; i++) { + index = strtoul(index_list[i], NULL, 0); + set_link_xdp_fd(index, -1, flags); + } + exit(0); +} + +/* Get the mac address of the interface given interface name */ +static long *getmac(char *iface) +{ + int fd; + struct ifreq ifr; + long *mac = NULL; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, iface, IFNAMSIZ - 1); + ioctl(fd, SIOCGIFHWADDR, &ifr); + mac = (long *)ifr.ifr_hwaddr.sa_data; + close(fd); + return mac; +} + +static int recv_msg(struct sockaddr_nl sock_addr, int sock) +{ + char *buf_ptr; + struct nlmsghdr *nh; + int len, nll = 0; + + buf_ptr = buf; + while (1) { + len = recv(sock, buf_ptr, sizeof(buf) - nll, 0); + if (len < 0) + return len; + + nh = (struct nlmsghdr *)buf_ptr; + + if (nh->nlmsg_type == NLMSG_DONE) + break; + buf_ptr += len; + nll += len; + if ((sock_addr.nl_groups & RTMGRP_NEIGH) == RTMGRP_NEIGH) + break; + + if ((sock_addr.nl_groups & RTMGRP_IPV4_ROUTE) == + RTMGRP_IPV4_ROUTE) + break; + } + return nll; +} + +/* Function to parse the route entry returned by netlink + * Updates the route entry related map entries + */ +static void read_route(struct nlmsghdr *nh, int nll) +{ + struct route_table { + int dst, gw, dst_len, iface, metric; + long *mac; + char iface_name[IFNAMSIZ]; + } route; + struct arp_table { + int dst; + long mac; + }; + + struct direct_map { + long mac; + int ifindex; + struct arp_table arp; + } direct_entry; + int i; + int rtm_family; + struct bpf_lpm_trie_key *prefix_key; + char dsts[24], gws[24], ifs[16], dsts_len[24], metrics[24]; + struct rtmsg *rt_msg; + int rtl; + struct rtattr *rt_attr; + + if (nh->nlmsg_type == RTM_DELROUTE) + printf("DELETING Route entry\n"); + else if (nh->nlmsg_type == RTM_GETROUTE) + printf("READING Route entry\n"); + else if (nh->nlmsg_type == RTM_NEWROUTE) + printf("NEW Route entry\n"); + else + printf("%d\n", nh->nlmsg_type); + + bzero(&route, sizeof(route)); + printf("Destination\tGateway\t\tGenmask\tMetric\tIface\n"); + for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) { + rt_msg = (struct rtmsg *)NLMSG_DATA(nh); + rtm_family = rt_msg->rtm_family; + if (rtm_family == AF_INET) + if (rt_msg->rtm_table != RT_TABLE_MAIN) + continue; + rt_attr = (struct rtattr *)RTM_RTA(rt_msg); + rtl = RTM_PAYLOAD(nh); + + for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) { + switch (rt_attr->rta_type) { + case NDA_DST: + sprintf(dsts, "%d", + *((int *)RTA_DATA(rt_attr))); + break; + case RTA_GATEWAY: + sprintf(gws, "%d", *((int *)RTA_DATA(rt_attr))); + break; + case RTA_OIF: + sprintf(ifs, "%d", *((int *)RTA_DATA(rt_attr))); + break; + case RTA_METRICS: + sprintf(metrics, "%d", + *((int *)RTA_DATA(rt_attr))); + default: + break; + } + } + sprintf(dsts_len, "%d", rt_msg->rtm_dst_len); + + route.dst = atoi(dsts); + route.dst_len = atoi(dsts_len); + route.gw = atoi(gws); + route.iface = atoi(ifs); + route.metric = atoi(metrics); + if_indextoname(route.iface, route.iface_name); + route.mac = getmac(route.iface_name); + printf("%x\t\t%x\t\t%d\t%d\t%d\n", route.dst, route.gw, + route.dst_len, route.metric, route.iface); + if (rtm_family == AF_INET) { + struct trie_value { + __u8 prefix[4]; + long value; + int gw; + int ifindex; + int metric; + } *prefix_value; + + prefix_key = alloca(sizeof(*prefix_key) + 3); + prefix_value = alloca(sizeof(*prefix_value)); + + prefix_key->prefixlen = 32; + prefix_key->prefixlen = route.dst_len; + direct_entry.mac = *route.mac & 0xffffffffffff; + direct_entry.ifindex = route.iface; + direct_entry.arp.mac = 0; + direct_entry.arp.dst = 0; + if (route.dst_len == 32) { + if (nh->nlmsg_type == RTM_DELROUTE) { + assert(bpf_map_delete_elem( + map_fd[3], + &route.dst + ) == 0); + } else { + if (bpf_map_lookup_elem(map_fd[2], + &route.dst, + &direct_entry.arp.mac + ) == 0) + direct_entry.arp.dst = route.dst; + + assert(bpf_map_update_elem(map_fd[3], + &route.dst, + &direct_entry, + 0) == 0); + } + } + for (i = 0; i < 4; i++) + prefix_key->data[i] = + (route.dst >> i * 8) % 0x100; + if (bpf_map_lookup_elem(map_fd[0], prefix_key, + prefix_value) < 0) { + for (i = 0; i < 4; i++) + prefix_value->prefix[i] = + prefix_key->data[i]; + prefix_value->value = + *route.mac & 0xffffffffffff; + prefix_value->ifindex = route.iface; + prefix_value->gw = route.gw; + prefix_value->metric = route.metric; + + assert(bpf_map_update_elem(map_fd[0], + prefix_key, + prefix_value, 0 + ) == 0); + } else { + if (nh->nlmsg_type == RTM_DELROUTE) { + printf("deleting entry\n"); + printf("prefix key=%d.%d.%d.%d/%d", + prefix_key->data[0], + prefix_key->data[1], + prefix_key->data[2], + prefix_key->data[3], + prefix_key->prefixlen); + assert(bpf_map_delete_elem(map_fd[0], + prefix_key + ) == 0); + /* Rereading the route table to check if + * there is an entry with the same + * prefix but a different metric as the + * deleted enty. + */ + get_route_table(AF_INET); + } else if (prefix_key->data[0] == + prefix_value->prefix[0] && + prefix_key->data[1] == + prefix_value->prefix[1] && + prefix_key->data[2] == + prefix_value->prefix[2] && + prefix_key->data[3] == + prefix_value->prefix[3] && + route.metric >= prefix_value->metric) { + continue; + } else { + for (i = 0; i < 4; i++) + prefix_value->prefix[i] = + prefix_key->data[i]; + prefix_value->value = + *route.mac & 0xffffffffffff; + prefix_value->ifindex = route.iface; + prefix_value->gw = route.gw; + prefix_value->metric = route.metric; + assert(bpf_map_update_elem( + map_fd[0], + prefix_key, + prefix_value, + 0) == 0); + } + } + } + bzero(&route, sizeof(route)); + bzero(dsts, sizeof(dsts)); + bzero(dsts_len, sizeof(dsts_len)); + bzero(gws, sizeof(gws)); + bzero(ifs, sizeof(ifs)); + bzero(&route, sizeof(route)); + } +} + +/* Function to read the existing route table when the process is launched*/ +static int get_route_table(int rtm_family) +{ + struct { + struct nlmsghdr nl; + struct rtmsg rt; + char buf[8192]; + } req; + + int sock, seq = 0; + struct sockaddr_nl sa; + struct msghdr msg; + struct iovec iov; + int ret = 0; + struct nlmsghdr *nh; + int nll; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + printf("open netlink socket: %s\n", strerror(errno)); + return -1; + } + bzero(&sa, sizeof(sa)); + sa.nl_family = AF_NETLINK; + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + printf("bind to netlink: %s\n", strerror(errno)); + ret = -1; + goto cleanup; + } + bzero(&req, sizeof(req)); + req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nl.nlmsg_type = RTM_GETROUTE; + + req.rt.rtm_family = rtm_family; + req.rt.rtm_table = RT_TABLE_MAIN; + req.nl.nlmsg_pid = 0; + req.nl.nlmsg_seq = ++seq; + bzero(&msg, sizeof(msg)); + iov.iov_base = (void *)&req.nl; + iov.iov_len = req.nl.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + printf("send to netlink: %s\n", strerror(errno)); + ret = -1; + goto cleanup; + } + bzero(buf, sizeof(buf)); + nll = recv_msg(sa, sock); + if (nll < 0) { + printf("recv from netlink: %s\n", strerror(nll)); + ret = -1; + goto cleanup; + } + nh = (struct nlmsghdr *)buf; + read_route(nh, nll); +cleanup: + close(sock); + return ret; +} + +/* Function to parse the arp entry returned by netlink + * Updates the arp entry related map entries + */ +static void read_arp(struct nlmsghdr *nh, int nll) +{ + struct arp_table { + int dst; + long mac; + } arp_entry; + struct direct_map { + long mac; + int ifindex; + struct arp_table arp; + } direct_entry; + + char dsts[24], mac[24]; + struct ndmsg *rt_msg; + int rtl, i = 0, ndm_family; + struct rtattr *rt_attr; + + if (nh->nlmsg_type == RTM_GETNEIGH) + printf("READING arp entry\n"); + printf("Address\tHwAddress\n"); + for (; NLMSG_OK(nh, nll); nh = NLMSG_NEXT(nh, nll)) { + i++; + rt_msg = (struct ndmsg *)NLMSG_DATA(nh); + rt_attr = (struct rtattr *)RTM_RTA(rt_msg); + ndm_family = rt_msg->ndm_family; + rtl = RTM_PAYLOAD(nh); + for (; RTA_OK(rt_attr, rtl); rt_attr = RTA_NEXT(rt_attr, rtl)) { + switch (rt_attr->rta_type) { + case NDA_DST: + sprintf(dsts, "%d", + *((int *)RTA_DATA(rt_attr))); + break; + case NDA_LLADDR: + sprintf(mac, "%ld", + *((long *)RTA_DATA(rt_attr))); + break; + default: + break; + } + } + arp_entry.dst = atoi(dsts); + arp_entry.mac = atol(mac); + printf("%x\t\t%lx\n", arp_entry.dst, arp_entry.mac); + if (ndm_family == AF_INET) { + if (bpf_map_lookup_elem(map_fd[3], &arp_entry.dst, + &direct_entry) == 0) { + if (nh->nlmsg_type == RTM_DELNEIGH) { + direct_entry.arp.dst = 0; + direct_entry.arp.mac = 0; + } else if (nh->nlmsg_type == RTM_NEWNEIGH) { + direct_entry.arp.dst = arp_entry.dst; + direct_entry.arp.mac = arp_entry.mac; + } + assert(bpf_map_update_elem(map_fd[3], + &arp_entry.dst, + &direct_entry, 0 + ) == 0); + bzero(&direct_entry, sizeof(direct_entry)); + } + if (nh->nlmsg_type == RTM_DELNEIGH) { + assert(bpf_map_delete_elem(map_fd[2], + &arp_entry.dst) == 0); + } else if (nh->nlmsg_type == RTM_NEWNEIGH) { + assert(bpf_map_update_elem(map_fd[2], + &arp_entry.dst, + &arp_entry.mac, 0 + ) == 0); + } + } + bzero(&arp_entry, sizeof(arp_entry)); + bzero(dsts, sizeof(dsts)); + } +} + +/* Function to read the existing arp table when the process is launched*/ +static int get_arp_table(int rtm_family) +{ + struct { + struct nlmsghdr nl; + struct ndmsg rt; + char buf[8192]; + } req; + + int sock, seq = 0; + struct sockaddr_nl sa; + struct msghdr msg; + struct iovec iov; + int ret = 0; + struct nlmsghdr *nh; + int nll; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + printf("open netlink socket: %s\n", strerror(errno)); + return -1; + } + bzero(&sa, sizeof(sa)); + sa.nl_family = AF_NETLINK; + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + printf("bind to netlink: %s\n", strerror(errno)); + ret = -1; + goto cleanup; + } + bzero(&req, sizeof(req)); + req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nl.nlmsg_type = RTM_GETNEIGH; + req.rt.ndm_state = NUD_REACHABLE; + req.rt.ndm_family = rtm_family; + req.nl.nlmsg_pid = 0; + req.nl.nlmsg_seq = ++seq; + bzero(&msg, sizeof(msg)); + iov.iov_base = (void *)&req.nl; + iov.iov_len = req.nl.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + printf("send to netlink: %s\n", strerror(errno)); + ret = -1; + goto cleanup; + } + bzero(buf, sizeof(buf)); + nll = recv_msg(sa, sock); + if (nll < 0) { + printf("recv from netlink: %s\n", strerror(nll)); + ret = -1; + goto cleanup; + } + nh = (struct nlmsghdr *)buf; + read_arp(nh, nll); +cleanup: + close(sock); + return ret; +} + +/* Function to keep track and update changes in route and arp table + * Give regular statistics of packets forwarded + */ +static int monitor_route(void) +{ + struct sockaddr_nl la, lr; + struct nlmsghdr *nh; + int nll, ret = 0; + const unsigned int nr_keys = 256; + int interval = 5; + unsigned int nr_cpus = bpf_num_possible_cpus(); + __u64 values[nr_cpus], prev[nr_keys][nr_cpus]; + __u32 key; + int i; + struct pollfd fds_route, fds_arp; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + printf("open netlink socket: %s\n", strerror(errno)); + return -1; + } + + fcntl(sock, F_SETFL, O_NONBLOCK); + bzero(&lr, sizeof(lr)); + lr.nl_family = AF_NETLINK; + lr.nl_groups = RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY; + if (bind(sock, (struct sockaddr *)&lr, sizeof(lr)) < 0) { + printf("bind to netlink: %s\n", strerror(errno)); + ret = -1; + goto cleanup; + } + fds_route.fd = sock; + fds_route.events = POLL_IN; + + sock_arp = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock_arp < 0) { + printf("open netlink socket: %s\n", strerror(errno)); + return -1; + } + + fcntl(sock_arp, F_SETFL, O_NONBLOCK); + bzero(&la, sizeof(la)); + la.nl_family = AF_NETLINK; + la.nl_groups = RTMGRP_NEIGH | RTMGRP_NOTIFY; + if (bind(sock_arp, (struct sockaddr *)&la, sizeof(la)) < 0) { + printf("bind to netlink: %s\n", strerror(errno)); + ret = -1; + goto cleanup; + } + fds_arp.fd = sock_arp; + fds_arp.events = POLL_IN; + + memset(prev, 0, sizeof(prev)); + do { + signal(SIGINT, close_and_exit); + signal(SIGTERM, close_and_exit); + + sleep(interval); + for (key = 0; key < nr_keys; key++) { + __u64 sum = 0; + + assert(bpf_map_lookup_elem(map_fd[1], &key, values) == 0); + for (i = 0; i < nr_cpus; i++) + sum += (values[i] - prev[key][i]); + if (sum) + printf("proto %u: %10llu pkt/s\n", + key, sum / interval); + memcpy(prev[key], values, sizeof(values)); + } + + bzero(buf, sizeof(buf)); + if (poll(&fds_route, 1, 3) == POLL_IN) { + nll = recv_msg(lr, sock); + if (nll < 0) { + printf("recv from netlink: %s\n", + strerror(nll)); + ret = -1; + goto cleanup; + } + + nh = (struct nlmsghdr *)buf; + printf("Routing table updated.\n"); + read_route(nh, nll); + } + bzero(buf, sizeof(buf)); + if (poll(&fds_arp, 1, 3) == POLL_IN) { + nll = recv_msg(la, sock_arp); + if (nll < 0) { + printf("recv from netlink: %s\n", + strerror(nll)); + ret = -1; + goto cleanup; + } + + nh = (struct nlmsghdr *)buf; + read_arp(nh, nll); + } + + } while (1); +cleanup: + close(sock); + return ret; +} + +int main(int ac, char **argv) +{ + char filename[256]; + int i = 1, index; + + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + printf("Entering user program\n"); + if (ac < 2) { + printf("usage: %s [-S] IFINDEX\n", argv[0]); + return 1; + } + if (!strcmp(argv[1], "-S")) { + flags = XDP_FLAGS_SKB_MODE; + total_ifindex = ac - 2; + index_list = (argv + 2); + } else { + flags = 0; + total_ifindex = ac - 1; + index_list = (argv + 1); + } +printf("Loading bpf program\n"); + if (load_bpf_file(filename)) { + printf("%s", bpf_log_buf); + return 1; + } + printf("\n**************loading bpf file*********************\n\n\n"); + if (!prog_fd[0]) { + printf("load_bpf_file: %s\n", strerror(errno)); + return 1; + } + + for (i = 0; i < total_ifindex; i++) { + index = strtoul(index_list[i], NULL, 0); + if (set_link_xdp_fd(index, prog_fd[0], flags) < 0) { + printf("link set xdp fd failed\n"); + return 1; + } + printf("Attached to %d\n", index); + } + signal(SIGINT, int_exit); + signal(SIGTERM, int_exit); + + printf("*******************ROUTE TABLE*************************\n\n\n"); + get_route_table(AF_INET); + printf("*******************ARP TABLE***************************\n\n\n"); + get_arp_table(AF_INET); + if (monitor_route() < 0) { + printf("Error in receiving route update"); + return 1; + } + + return 0; +} -- 1.7.1