Re: [PATCH] batman-adv: Remove unused variable
Hi Emil, On Wed, Feb 06, 2013 at 05:37:41 +0100, Emil Goode wrote: > The commit ed242d01 removed a node parameter from iterators. > This patch removes a hlist_node struct that is no longer used. > > Sparse gives a warning: > > net/batman-adv/originator.c:411:21: warning: > unused variable ‘node_tmp’ [-Wunused-variable] > > Signed-off-by: Emil Goode On which tree did you base your patch? I cannot find the commit you are referring to (ed242d01) and the patch does not apply (neither on net nor net-next). Other than that the variable is used a couple of lines below (in the code we have)... Regards, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpyR7hC9GdIq.pgp Description: PGP signature
Re: [PATCH] batman-adv: Remove unused variable
Hi Emil, On Wed, Feb 06, 2013 at 06:55:53 +0100, Emil Goode wrote: > Hi Antonio, > > The commit ed242d01 is in the linux-next tree and my patch depends on > that commit. > Yes, you are right. I was not aware of this commit, because the author did not CC us (neither netdev) even if he changed the code we maintain. As far as I can see that commit is in linux-next only. Therefore it must be applied there. However, I'd like you to change the commit message and write the full hash of the commit you are referring to and its subject ("LIKE THIS"). This would make it easier to find it. Regards, > Best regards, Emil > > On Wed, 2013-02-06 at 18:22 +0100, Antonio Quartulli wrote: > > Hi Emil, > > > > On Wed, Feb 06, 2013 at 05:37:41 +0100, Emil Goode wrote: > > > The commit ed242d01 removed a node parameter from iterators. > > > This patch removes a hlist_node struct that is no longer used. > > > > > > Sparse gives a warning: > > > > > > net/batman-adv/originator.c:411:21: warning: > > > unused variable ‘node_tmp’ [-Wunused-variable] > > > > > > Signed-off-by: Emil Goode > > > > On which tree did you base your patch? > > I cannot find the commit you are referring to (ed242d01) and the patch does > > not > > apply (neither on net nor net-next). > > > > Other than that the variable is used a couple of lines below (in the code we > > have)... > > > > Regards, > > > > -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpoE3vTCMMGH.pgp Description: PGP signature
Re: [PATCH] batman-adv: Remove unused variable
Hi Emil, On Wed, Feb 06, 2013 at 10:59:05 +0100, Emil Goode wrote: > Hi Antonio, > > If it is easier I can keep an eye on when the commit lands in the > net-next tree and then resend a modified version of the patch. > Or do you want me to resend it now? > Well, that commit should be merged in net-next after the next merge window. You can resend it now, but be sure to CC Stephen Rothwell (the linux-next maintainer) and to clearly state that the patch is for linux-next. Thanks. Cheers, p.s. try to do not top-post please :-) > Best regards, Emil > > On Wed, 2013-02-06 at 20:55 +0100, Antonio Quartulli wrote: > > Hi Emil, > > > > On Wed, Feb 06, 2013 at 06:55:53 +0100, Emil Goode wrote: > > > Hi Antonio, > > > > > > The commit ed242d01 is in the linux-next tree and my patch depends on > > > that commit. > > > > > > > Yes, you are right. I was not aware of this commit, because the author > > did not CC us (neither netdev) even if he changed the code we maintain. > > > > As far as I can see that commit is in linux-next only. > > Therefore it must be applied there. > > > > However, I'd like you to change the commit message and write the full hash > > of > > the commit you are referring to and its subject ("LIKE THIS"). This would > > make > > it easier to find it. > > > > > > Regards, > > > > > Best regards, Emil > > > > > > On Wed, 2013-02-06 at 18:22 +0100, Antonio Quartulli wrote: > > > > Hi Emil, > > > > > > > > On Wed, Feb 06, 2013 at 05:37:41 +0100, Emil Goode wrote: > > > > > The commit ed242d01 removed a node parameter from iterators. > > > > > This patch removes a hlist_node struct that is no longer used. > > > > > > > > > > Sparse gives a warning: > > > > > > > > > > net/batman-adv/originator.c:411:21: warning: > > > > > unused variable ‘node_tmp’ [-Wunused-variable] > > > > > > > > > > Signed-off-by: Emil Goode > > > > > > > > On which tree did you base your patch? > > > > I cannot find the commit you are referring to (ed242d01) and the patch > > > > does not > > > > apply (neither on net nor net-next). > > > > > > > > Other than that the variable is used a couple of lines below (in the > > > > code we > > > > have)... > > > > > > > > Regards, > > > > > > > > > > > > > > -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpdBDmKB0jSQ.pgp Description: PGP signature
Re: [PATCH v2] batman-adv: Remove unused variable
On Thu, Feb 07, 2013 at 12:32:37AM +0100, Emil Goode wrote: > The below commit removed one node parameter from iterators. > ed242d01bbe22ea0877472db49b2752d866c921c > (hlist: drop the node parameter from iterators) > > This patch removes a hlist_node struct that is no longer used. > > Sparse gives a warning: > > net/batman-adv/originator.c:411:21: warning: > unused variable ‘node_tmp’ [-Wunused-variable] > > Signed-off-by: Emil Goode Acked-by: Antonio Quartulli Thanks Emil Cheers, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpK76rjWJcDW.pgp Description: PGP signature
Re: batman-adv: gpf in batadv_slide_own_bcast_window
Hi Sasha and thank you very much for reporting this issue. IIRC this is similar to a bug you already reported in the past. This bug should be the result of a race condition batman-adv has in the hard-interface handling code (this is why it has been triggered while removing eth0). Now that the rtnl-deadlock has been solved I think we can try to further investigate on this bug and try to find a solution..though it will not be easy as it probably requires another lock to protect the hard-interface during this operations. If you have any fix proposal feel free to contribute! Cheers, On Fri, Feb 22, 2013 at 11:54:07AM -0500, Sasha Levin wrote: > Hi all, > > While fuzzing with trinity inside a KVM tools guest running latest -next > kernel > I've stumbled on the following: > > [ 3148.615130] batman_adv: <98>\^?: Removing interface: eth0 > [ 3148.991938] general protection fault: [#1] PREEMPT SMP DEBUG_PAGEALLOC > [ 3148.993736] Dumping ftrace buffer: > [ 3148.997554](ftrace buffer empty) > [ 3148.998426] Modules linked in: > [ 3148.999135] CPU 3 > [ 3148.999606] Pid: 6, comm: kworker/u:0 Tainted: GW > 3.8.0-next-20130222-sasha-00038-gba27e20-dirty #11 > [ 3149.001223] RIP: 0010:[] [] > batadv_slide_own_bcast_window+0xb8/0x2b0 > [ 3149.001223] RSP: 0018:8800b9f4fc58 EFLAGS: 00010246 > [ 3149.001223] RAX: RBX: RCX: > 0001 > [ 3149.001223] RDX: RSI: 0001 RDI: > 0001 > [ 3149.001223] RBP: 8800b9f4fcb8 R08: 0002 R09: > 8800b9f63950 > [ 3149.001223] R10: R11: R12: > 8800abad2238 > [ 3149.001223] R13: 6b6b6b6b6b6b865b R14: 88004c13cda0 R15: > 0001 > [ 3149.001223] FS: () GS:8800bbc0() > knlGS: > [ 3149.001223] CS: 0010 DS: ES: CR0: 80050033 > [ 3149.001223] CR2: 7f006711f1d0 CR3: 8258e000 CR4: > 000406e0 > [ 3149.001223] DR0: DR1: DR2: > > [ 3149.001223] DR3: DR6: 0ff0 DR7: > 0400 > [ 3149.001223] Process kworker/u:0 (pid: 6, threadinfo 8800b9f4e000, task > 8800b9f63000) > [ 3149.001223] Stack: > [ 3149.001223] 83d21760 8800b9f63000 8800abad2238 > > [ 3149.001223] 880068f6c438 035e0001 8800b9f4fc98 > > [ 3149.001223] 8800abad2238 88004c13c2a0 88004c13cda0 > 0001 > [ 3149.001223] Call Trace: > [ 3149.001223] [] ? > batadv_slide_own_bcast_window+0x40/0x2b0 > [ 3149.001223] [] batadv_iv_ogm_schedule+0x254/0x300 > [ 3149.001223] [] ? batadv_iv_ogm_queue_add+0x710/0x710 > [ 3149.001223] [] ? local_bh_enable_ip+0xef/0x150 > [ 3149.001223] [] > batadv_send_outstanding_bat_ogm_packet+0xc5/0xf0 > [ 3149.001223] [] process_one_work+0x366/0x6a0 > [ 3149.001223] [] ? process_one_work+0x228/0x6a0 > [ 3149.001223] [] worker_thread+0x238/0x370 > [ 3149.001223] [] ? rescuer_thread+0x310/0x310 > [ 3149.001223] [] kthread+0xe3/0xf0 > [ 3149.001223] [] ? flush_kthread_work+0x1f0/0x1f0 > [ 3149.001223] [] ret_from_fork+0x7c/0xb0 > [ 3149.001223] [] ? flush_kthread_work+0x1f0/0x1f0 > [ 3149.001223] Code: 31 4b fd 85 c0 74 24 48 c7 c2 50 cd bd 84 be 02 03 00 00 > 48 c7 c7 b4 da bd 84 c6 05 14 ab 16 02 01 e8 ed 16 > 46 fd 0f 1f 44 00 00 <49> 8b 55 00 48 89 55 b8 e8 0b 55 41 fd 85 c0 74 37 80 > 3d ee aa > [ 3149.001223] RIP [] > batadv_slide_own_bcast_window+0xb8/0x2b0 > [ 3149.001223] RSP > [ 3149.105631] ---[ end trace ba69e369627c73e7 ]--- > > Rip points to: > > for (i = 0; i < hash->size; i++) { > head = &hash->table[i]; > > rcu_read_lock(); > hlist_for_each_entry_rcu(orig_node, head, hash_entry) { <--- > here > spin_lock_bh(&orig_node->ogm_cnt_lock); > word_index = hard_iface->if_num * BATADV_NUM_WORDS; > word = &(orig_node->bcast_own[word_index]); > > > Thanks, > Sasha -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpYgssuyHDAq.pgp Description: PGP signature
Re: [PATCH] batman: Remove reference to compare_ether_addr
On Sun, Sep 01, 2013 at 03:45:08PM -0700, Joe Perches wrote: > This function is being removed, rename the reference. > > Signed-off-by: Joe Perches Acked-by: Antonio Quartulli Thanks a lot Joe -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara signature.asc Description: Digital signature
Re: [PATCH -v2 19/26] batman-adv: rename random32() to prandom_u32()
On Thu, Jan 03, 2013 at 09:19:15PM +0900, Akinobu Mita wrote: > Use more preferable function name which implies using a pseudo-random > number generator. > > Signed-off-by: Akinobu Mita > Acked-by: Antonio Quartulli > Cc: Marek Lindner > Cc: Simon Wunderlich > Cc: Antonio Quartulli > Cc: b.a.t.m@lists.open-mesh.org > Cc: "David S. Miller" > Cc: net...@vger.kernel.org > --- Hello Akinobu, as you can see in <201301021952.49979.lindner_ma...@yahoo.de>, Marek Lindner already applied this change onto our tree. You didn't need to resend this patch to netdev, it will be sent by us through a future pull request. Thanks a lot. Cheers, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgp__3FYamFN8.pgp Description: PGP signature
Re: [PATCH 19/29] batman-adv: fix random jitter calculation
On Mon, Dec 24, 2012 at 11:14:06AM +0900, Akinobu Mita wrote: > batadv_iv_ogm_emit_send_time() attempts to calculates a random integer > in the range of 'orig_interval +- BATADV_JITTER' by the below lines. > > msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER; > msecs += (random32() % 2 * BATADV_JITTER); > > But it actually gets 'orig_interval' or 'orig_interval - BATADV_JITTER' > because '%' and '*' have same precedence and associativity is > left-to-right. > > This adds the parentheses at the appropriate position so that it matches > original intension. > > Signed-off-by: Akinobu Mita > Cc: Marek Lindner > Cc: Simon Wunderlich > Cc: Antonio Quartulli > Cc: b.a.t.m....@lists.open-mesh.org > Cc: "David S. Miller" > Cc: net...@vger.kernel.org > --- Acked-by: Antonio Quartulli But I would suggest to apply this change to net, since it is a fix. Cheers, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpZ1jxN4TR8b.pgp Description: PGP signature
Re: [PATCH 20/29] batman-adv: rename random32() to prandom_u32()
On Mon, Dec 24, 2012 at 11:14:07AM +0900, Akinobu Mita wrote: > Use more preferable function name which implies using a pseudo-random > number generator. > > Signed-off-by: Akinobu Mita > Cc: Marek Lindner > Cc: Simon Wunderlich > Cc: Antonio Quartulli > Cc: b.a.t.m@lists.open-mesh.org > Cc: "David S. Miller" > Cc: net...@vger.kernel.org Acked-by: Antonio Quartulli -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpgEUh68eOJQ.pgp Description: PGP signature
Re: [PATCH] net, batman: don't crash on zero length strings in routing_algo
On Mon, Nov 19, 2012 at 03:08:15PM -0500, Sasha Levin wrote: > The code that works with routing_algo assumes that the string passed is non > empty, this assumption is wrong: > > sh-4.2# echo -ne '\0' > /sys/module/batman_adv/parameters/routing_algo > [ 34.531340] BUG: unable to handle kernel paging request at 880015142fff [CUT] > [ 34.550025] ---[ end trace 6c53b662c574774b ]--- > > Signed-off-by: Sasha Levin Hello Sasha, thank you very much for fixing this bug! However, any patch sent against the B.A.T.M.A.N.-Advanced code should have a subject starting with "batman-adv:". Other than that I think this kind of patch doesn't really need to report the entire kernel message: a more exhaustive commit message is enough (e.g. use function names). I personally needed to read the patch before understanding what you were trying to explain in the message. Then, we usually pick this patches up in our repo and then we send them as batch to the networking tree via pull request, therefore you can also skip the netdev ml when sending the fixes. Thank you very much! Regards, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpCP2otMCxgv.pgp Description: PGP signature
Re: linux-next: build failure after merge of the net-next tree
Hi Stephen, On Fri, Apr 19, 2013 at 01:21:27PM +1000, Stephen Rothwell wrote: > > Caused by commits 612d2b4fe0a1 ("batman-adv: network coding - save > overheard and tx packets for decoding") and 2df5278b0267 ("batman-adv: > network coding - receive coded packets and decode them") from the > net-next tree interacting with commit fe8a93b95145 ("batman-adv: make > is_my_mac() check for the current mesh only") from the net tree. > > I added the following merge fix patch and can carry it as necessary: thank you very much for taking care of this. However, as I wrote to David in the pull request which generated this noise (http://patchwork.ozlabs.org/patch/237363/), I am now going to send a second pull request which fixes this problem properly and adds the related kernel doc. Your patch looks good, but I'd prefer to send my already prepared pull request. Is it ok with you? Cheers, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara signature.asc Description: Digital signature
Re: WARNING: at net/batman-adv/hard-interface.c:92 batadv_is_on_batman_iface()
Hello all, On Sat, Mar 30, 2013 at 05:36:35PM +0800, Fengguang Wu wrote: > Greetings, > > I got the below WARNING in net-next/master (head commit f498354793d5) > and the first bad commit is > > commit c54419321455631079c7d6e60bc732dd0c5914c5 > Author: Pravin B Shelar > Date: Mon Mar 25 14:49:35 2013 + > > GRE: Refactor GRE tunneling code. > > Following patch refactors GRE code into ip tunneling code and GRE > specific code. Common tunneling code is moved to ip_tunnel module. > ip_tunnel module is written as generic library which can be used > by different tunneling implementations. > > ip_tunnel module contains following components: > - packet xmit and rcv generic code. xmit flow looks like >(gre_xmit/ipip_xmit)->ip_tunnel_xmit->ip_local_out. > - hash table of all devices. > - lookup for tunnel devices. > - control plane operations like device create, destroy, ioctl, netlink >operations code. > - registration for tunneling modules, like gre, ipip etc. > - define single pcpu_tstats dev->tstats. > - struct tnl_ptk_info added to pass parsed tunnel packet parameters. > > ipip.h header is renamed to ip_tunnel.h > > Signed-off-by: Pravin B Shelar > Signed-off-by: David S. Miller > > [ 27.507156] libceph: loaded (mon/osd proto 15/24) > [ 27.515409] [ cut here ] > [ 27.518917] WARNING: at > /c/kernel-tests/src/stable/net/batman-adv/hard-interface.c:92 > batadv_is_on_batman_iface+0x5c/0x7a() > [ 27.521935] Hardware name: Bochs > [ 27.525356] Cannot find parent device > [ 27.528296] Modules linked in: > [ 27.529923] Pid: 1, comm: swapper/0 Not tainted 3.9.0-rc4-00896-g03ba910 > #794 > [ 27.531747] Call Trace: > [ 27.534683] [] warn_slowpath_common+0x83/0x9e > [ 27.536338] [] warn_slowpath_fmt+0x46/0x48 > [ 27.537872] [] ? rcu_read_unlock+0x1c/0x2d > [ 27.539441] [] ? local_clock+0x19/0x52 > [ 27.541023] [] batadv_is_on_batman_iface+0x5c/0x7a > [ 27.542803] [] batadv_hard_if_event+0x8f/0x285 > [ 27.544551] [] register_netdevice_notifier+0x71/0x17e > [ 27.550617] [] ? batadv_iv_init+0x3f/0x3f > [ 27.552248] [] batadv_init+0xe4/0x104 > [ 27.553770] [] do_one_initcall+0x7f/0x13d > [ 27.555386] [] kernel_init_freeable+0x141/0x1d0 > [ 27.557177] [] ? do_early_param+0x8c/0x8c > [ 27.558813] [] ? rest_init+0xda/0xda > [ 27.560348] [] kernel_init+0xe/0xdb > [ 27.561813] [] ret_from_fork+0x7c/0xb0 > [ 27.563356] [] ? rest_init+0xda/0xda > [ 27.564974] ---[ end trace f78f9f0651ffcb0b ]--- > The reason why batman-adv it raising this warning is because this call is returning NULL: dev_get_by_index(&init_net, net_dev->iflink); net_dev is an interface that has been registered now and batman-adv is trying to analyse it to decide if it is a potential candidate for its virtual device or not. To the best of my knowledge, if the function above is returning NULL, it means that the iflink attribute contains a broken value. Am I wrong or iflink should never contain 0? If there is no parent device it should contain the same value of ifindex. Right? Regards, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpplpg2A4v1y.pgp Description: PGP signature
[PATCH] ip_gre: don't overwrite iflink during net_dev init
iflink is currently set to 0 in __gre_tunnel_init(). This function is invoked in gre_tap_init() and ipgre_tunnel_init() which are both used to initialise the ndo_init field of the respective net_device_ops structs (ipgre.. and gre_tap..) used by GRE interfaces. However, in netdevice_register() iflink is first set to -1, then ndo_init is invoked and then iflink is assigned to a proper value if and only if it still was -1. Assigning 0 to iflink in ndo_init is therefore first preventing netdev_register() to correctly assign it a proper value and then breaking iflink at all since 0 has not correct meaning. Fix this by removing the iflink assignment in __gre_tunnel_init(). Introduced by c54419321455631079c7d6e60bc732dd0c5914c5 ("GRE: Refactor GRE tunneling code.") Reported-by: Fengguang Wu Cc: Pravin B Shelar Cc: "David S. Miller" Signed-off-by: Antonio Quartulli --- net/ipv4/ip_gre.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index ad662e9..e5dfd28 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -660,7 +660,6 @@ static void __gre_tunnel_init(struct net_device *dev) dev->needed_headroom= LL_MAX_HEADER + sizeof(struct iphdr) + 4; dev->mtu= ETH_DATA_LEN - sizeof(struct iphdr) - 4; - dev->iflink = 0; dev->features |= NETIF_F_NETNS_LOCAL | GRE_FEATURES; dev->hw_features|= GRE_FEATURES; -- 1.8.1.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH] batman-adv: Fix mem leak in the batadv_tt_local_event() function
On Tue, Aug 07, 2012 at 08:32:34PM +0200, Jesper Juhl wrote: > Memory is allocated for 'tt_change_node' with kmalloc(). > 'tt_change_node' may go out of scope really being used for anything > (except have a few members initialized) if we hit the 'del:' label. > This patch makes sure we free the memory in that case. > > Signed-off-by: Jesper Juhl Acked-by: Antonio Quartulli Thank you very much Jepser! -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpDpRK1l8sVx.pgp Description: PGP signature
Re: [PATCH -v2 19/26] batman-adv: rename random32() to prandom_u32()
On Fri, Jan 04, 2013 at 10:50:41PM +0900, Akinobu Mita wrote: > 2013/1/4 Antonio Quartulli : > > On Thu, Jan 03, 2013 at 09:19:15PM +0900, Akinobu Mita wrote: > >> Use more preferable function name which implies using a pseudo-random > >> number generator. > >> > >> Signed-off-by: Akinobu Mita > >> Acked-by: Antonio Quartulli > >> Cc: Marek Lindner > >> Cc: Simon Wunderlich > >> Cc: Antonio Quartulli > >> Cc: b.a.t.m@lists.open-mesh.org > >> Cc: "David S. Miller" > >> Cc: net...@vger.kernel.org > >> --- > > > > Hello Akinobu, > > > > as you can see in <201301021952.49979.lindner_ma...@yahoo.de>, Marek Lindner > > already applied this change onto our tree. You didn't need to resend this > > patch > > to netdev, it will be sent by us through a future pull request. > > Yes. I read Marek's email. But I included it in v2 again in order not > to break the build. Because it doesn't show up the latest linux-next yet > and the last patch 26/26 removes random32() and srandom32(). > > But I should have mentioned it and trimmed the Cc list. > Sorry for the confusion. Ok..everything is fine as soon David won't apply this patch. Cheers, -- Antonio Quartulli ..each of us alone is worth nothing.. Ernesto "Che" Guevara pgpwsSCudVeSV.pgp Description: PGP signature
Re: [PATCH] batman-adv:Make the function batadv_is_my_mac bool
Nicholas, On 11/06/15 04:25, Nicholas Krause wrote: > This makes the function batadv_is_my_mac bool now due > to this particular function only returning either one > or zero as its return value. > > Signed-off-by: Nicholas Krause NAK. we already have ("batman-adv: main, Convert is_my_mac() to bool") in net-next that does exactly the same. I guess you did not pull the latest changes before writing this patch? Cheers, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
[PATCH] can: avoid double unlikely() notation when using IS_ERR()
The definition of IS_ERR() already applies the unlikely() notation when checking the error status of the passed pointer. For this reason there is no need to have the same notation outside of IS_ERR() itself. Clean up code by removing redundant notation. Signed-off-by: Antonio Quartulli --- drivers/net/can/rx-offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c index 450c5cfcb3fc..3c1912c0430b 100644 --- a/drivers/net/can/rx-offload.c +++ b/drivers/net/can/rx-offload.c @@ -157,7 +157,7 @@ can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n) /* There was a problem reading the mailbox, propagate * error value. */ - if (unlikely(IS_ERR(skb))) { + if (IS_ERR(skb)) { offload->dev->stats.rx_dropped++; offload->dev->stats.rx_fifo_errors++; -- 2.29.2
[PATCH] dm ebs: avoid double unlikely() notation when using IS_ERR()
The definition of IS_ERR() already applies the unlikely() notation when checking the error status of the passed pointer. For this reason there is no need to have the same notation outside of IS_ERR() itself. Clean up code by removing redundant notation. Signed-off-by: Antonio Quartulli --- drivers/md/dm-ebs-target.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-ebs-target.c b/drivers/md/dm-ebs-target.c index cb85610527c2..55bcfb74f51f 100644 --- a/drivers/md/dm-ebs-target.c +++ b/drivers/md/dm-ebs-target.c @@ -86,7 +86,7 @@ static int __ebs_rw_bvec(struct ebs_c *ec, int rw, struct bio_vec *bv, struct bv else ba = dm_bufio_new(ec->bufio, block, &b); - if (unlikely(IS_ERR(ba))) { + if (IS_ERR(ba)) { /* * Carry on with next buffer, if any, to issue all possible * data but return error. -- 2.29.2
[PATCH] vxlan: avoid double unlikely() notation when using IS_ERR()
The definition of IS_ERR() already applies the unlikely() notation when checking the error status of the passed pointer. For this reason there is no need to have the same notation outside of IS_ERR() itself. Clean up code by removing redundant notation. Signed-off-by: Antonio Quartulli --- drivers/net/vxlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index e1e44d68ac4b..a8ad710629e6 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2477,7 +2477,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, ndst = ipv6_stub->ipv6_dst_lookup_flow(vxlan->net, sock6->sock->sk, &fl6, NULL); - if (unlikely(IS_ERR(ndst))) { + if (IS_ERR(ndst)) { netdev_dbg(dev, "no route to %pI6\n", daddr); return ERR_PTR(-ENETUNREACH); } -- 2.29.2
[PATCH] wireguard: avoid double unlikely() notation when using IS_ERR()
The definition of IS_ERR() already applies the unlikely() notation when checking the error status of the passed pointer. For this reason there is no need to have the same notation outside of IS_ERR() itself. Clean up code by removing redundant notation. Signed-off-by: Antonio Quartulli --- drivers/net/wireguard/device.c | 2 +- drivers/net/wireguard/socket.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireguard/device.c b/drivers/net/wireguard/device.c index a3ed49cd95c3..cd51a2afa28e 100644 --- a/drivers/net/wireguard/device.c +++ b/drivers/net/wireguard/device.c @@ -157,7 +157,7 @@ static netdev_tx_t wg_xmit(struct sk_buff *skb, struct net_device *dev) } else { struct sk_buff *segs = skb_gso_segment(skb, 0); - if (unlikely(IS_ERR(segs))) { + if (IS_ERR(segs)) { ret = PTR_ERR(segs); goto err_peer; } diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index c33e2c81635f..e9c35130846c 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -71,7 +71,7 @@ static int send4(struct wg_device *wg, struct sk_buff *skb, ip_rt_put(rt); rt = ip_route_output_flow(sock_net(sock), &fl, sock); } - if (unlikely(IS_ERR(rt))) { + if (IS_ERR(rt)) { ret = PTR_ERR(rt); net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n", wg->dev->name, &endpoint->addr, ret); @@ -138,7 +138,7 @@ static int send6(struct wg_device *wg, struct sk_buff *skb, } dst = ipv6_stub->ipv6_dst_lookup_flow(sock_net(sock), sock, &fl, NULL); - if (unlikely(IS_ERR(dst))) { + if (IS_ERR(dst)) { ret = PTR_ERR(dst); net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n", wg->dev->name, &endpoint->addr, ret); -- 2.29.2
Re: [PATCH v3] ieee80211: Print human-readable disassoc/deauth reason codes
On 11/02/14 17:37, Calvin Owens wrote: > Create a function to return a descriptive string for each reason code, > and print that in addition to the numeric value in the kernel log. These > codes are easily found on popular search engines, but one is generally > not able to access the internet when dealing with wireless connectivity > issues. > > Changes in v2: Refactored array of strings into switch statement. > Changes in v3: Fix style problem, use simplifying macro for switch > statement, eliminate temporary enum variable. > > Signed-off-by: Calvin Owens > --- > include/net/mac80211.h | 10 + > net/mac80211/main.c| 57 > ++ > net/mac80211/mlme.c| 12 +-- > 3 files changed, 73 insertions(+), 6 deletions(-) > > diff --git a/include/net/mac80211.h b/include/net/mac80211.h > index f4ab2fb..d18acfe 100644 > --- a/include/net/mac80211.h > +++ b/include/net/mac80211.h > @@ -2971,6 +2971,16 @@ struct ieee80211_ops { > */ > struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, > const struct ieee80211_ops *ops); > +/** > + * ieee80211_get_reason_code_string - Get human readable reason code > + * > + * This function returns a string describing the @reason_code. > + * > + * @reason_code: Reason code Kerneldoc is not properly formatted here. The "@argument:" clause should be on the line right after the function name (as explained in Documentation/kernel-doc-nano-HOWTO.txt), e.g.: /** * function_name - blabla * @arg: I am a good arg description * Cheers, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [B.A.T.M.A.N.] [PATCH v3.6 06/19] batman-adv: use batadv_compare_eth for concise
On 08/01/14 03:53, Ding Tianhong wrote: > It is better to use batadv_compate_eth instead of memcpy for > concise style. > > Cc: Marek Lindner > Cc: Simon Wunderlich > Cc: Antonio Quartulli > Cc: "David S. Miller" > Cc: b.a.t.m@lists.open-mesh.org > Cc: net...@vger.kernel.org > Cc: linux-kernel@vger.kernel.org > Signed-off-by: Tan Xiaojun > Signed-off-by: Ding Tianhong > Acked-by: Antonio Quartulli Am I wrong or this patch has already been merged in net-next (together with other patches from this series)? Regards, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH linux-next] net: batman-adv: use "__packed __aligned(2)" for each structure instead of "__packed(2)" region
On 18/01/14 12:31, Chen Gang wrote: > Unfortunately, not all compilers assumes the structures within a pack > region also need be packed (e.g. metag), so need add a pack explicitly > to satisfy all compilers. > > The related error (under metag with allmodconfig): > > MODPOST 2952 modules > ERROR: "__compiletime_assert_431" [net/batman-adv/batman-adv.ko] undefined! > ERROR: "__compiletime_assert_432" [net/batman-adv/batman-adv.ko] undefined! > ERROR: "__compiletime_assert_429" [net/batman-adv/batman-adv.ko] undefined! > ERROR: "__compiletime_assert_428" [net/batman-adv/batman-adv.ko] undefined! > ERROR: "__compiletime_assert_423" [net/batman-adv/batman-adv.ko] undefined! > > > Signed-off-by: Chen Gang David, what do you think about this change? Can "__packed __aligned(2)" generate a different structure padding than "#pragma pack(2)" ? I am not really sure about the difference between the two. But if we have the possibility that the padding may change then this patch should go into net, otherwise we will have a protocol compatibility problem between 3.13 and 3.14. Cheers, > --- > net/batman-adv/packet.h | 16 ++-- > 1 file changed, 6 insertions(+), 10 deletions(-) > > diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h > index 0a381d1..9206b48 100644 > --- a/net/batman-adv/packet.h > +++ b/net/batman-adv/packet.h > @@ -154,7 +154,6 @@ enum batadv_tvlv_type { > BATADV_TVLV_ROAM= 0x05, > }; > > -#pragma pack(2) > /* the destination hardware field in the ARP frame is used to > * transport the claim type and the group id > */ > @@ -162,8 +161,7 @@ struct batadv_bla_claim_dst { > uint8_t magic[3]; /* FF:43:05 */ > uint8_t type; /* bla_claimframe */ > __be16 group; /* group id */ > -}; > -#pragma pack() > +} __packed __aligned(2); > > /** > * struct batadv_ogm_packet - ogm (routing protocol) packet > @@ -281,7 +279,6 @@ struct batadv_icmp_packet_rr { > * misalignment of the payload after the ethernet header. It may also lead to > * leakage of information when the padding it not initialized before sending. > */ > -#pragma pack(2) > > /** > * struct batadv_unicast_packet - unicast packet for network payload > @@ -300,7 +297,7 @@ struct batadv_unicast_packet { > /* "4 bytes boundary + 2 bytes" long to make the payload after the >* following ethernet header again 4 bytes boundary aligned >*/ > -}; > +} __packed __aligned(2); > > /** > * struct batadv_unicast_4addr_packet - extended unicast packet > @@ -316,7 +313,7 @@ struct batadv_unicast_4addr_packet { > /* "4 bytes boundary + 2 bytes" long to make the payload after the >* following ethernet header again 4 bytes boundary aligned >*/ > -}; > +} __packed __aligned(2); > > /** > * struct batadv_frag_packet - fragmented packet > @@ -347,7 +344,7 @@ struct batadv_frag_packet { > uint8_t orig[ETH_ALEN]; > __be16 seqno; > __be16 total_size; > -}; > +} __packed __aligned(2); > > /** > * struct batadv_bcast_packet - broadcast packet for network payload > @@ -368,7 +365,7 @@ struct batadv_bcast_packet { > /* "4 bytes boundary + 2 bytes" long to make the payload after the >* following ethernet header again 4 bytes boundary aligned >*/ > -}; > +} __packed __aligned(2); > > /** > * struct batadv_coded_packet - network coded packet > @@ -404,9 +401,8 @@ struct batadv_coded_packet { > uint8_t second_orig_dest[ETH_ALEN]; > __be32 second_crc; > __be16 coded_len; > -}; > +} __packed __aligned(2); > > -#pragma pack() > > /** > * struct batadv_unicast_tvlv - generic unicast packet with tvlv payload > -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH linux-next] net: batman-adv: use "__packed __aligned(2)" for each structure instead of "__packed(2)" region
On 19/01/14 02:10, James Hogan wrote: > > It appears that the following gcc patch adds support for #pragma pack: > http://gcc.gnu.org/ml/gcc-patches/2006-10/msg01115.html > > I gave it a quick spin on metag gcc (which is unfortunately stuck on an old > version) and it seems to fix my simple test case so that #pragma pack(2) > becomes equivalent to __packed __aligned(2) (for sizeof and __alignof__). > Then I personally think that it is better to fix metag gcc instead of changing the kernel. Actually there are many different spots where "#pragma pack" is used. batman-adv is just the only one having compile time checks for structure sizes. > > However, the __packed and __aligned are linux specific macros to abstract > compiler details, whereas #pragma pack appears to be a compiler-specific > WIN32 > style equivalent to GCC's __attribute__((packed)) and > __attribute__((aligned(2))) (these are what __packed and __aligned use in > compiler-gcc.h). > > Therefore I believe using the Linux abstractions is still more correct here. If you really think so, I'd suggest to grep in the kernel and catch all the other occurrences of "#pragma pack" and change them all (assuming that using __attribute__((aligned(2))) is the way to go). Cheers, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH] batman-adv: fix potential NULL pointer dereferencing
On 22/09/14 11:11, Mario Pirker wrote: > From b451e7317148e18bf6c5c8fd747d79ab34260354 Mon Sep 17 00:00:00 2001 > From: Mario Pirker > Date: Tue, 16 Sep 2014 17:55:13 +0200 > Subject: [PATCH] batman-adv: fix potential NULL pointer dereferencing > > The call batadv_gw_node_get may return NULL. The return value has to > be sanity checked before the pointer is dereferenced. > > Signed-off-by: Mario Pirker > --- We already have a patch queued to fix this issue (http://permalink.gmane.org/gmane.org.freifunk.batman/12357). Actually it is not about a missing check but it is more about a typo in the check right below the one you added. Cheers, > net/batman-adv/gateway_client.c | 5 + > 1 file changed, 5 insertions(+) > > diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c > index 90cff58..7ee0913 100644 > --- a/net/batman-adv/gateway_client.c > +++ b/net/batman-adv/gateway_client.c > @@ -810,6 +810,11 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, > goto out; > > gw_node = batadv_gw_node_get(bat_priv, orig_dst_node); > + > + /* gw_node can be NULL. We need to check before dereferencing */ > + if (gw_node == NULL) > + goto out; > + > if (!gw_node->bandwidth_down == 0) > goto out; > > -- > 1.8.1.4 > -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [B.A.T.M.A.N.] [PATCH 09/22] batman-adv: Replace strnicmp with strncasecmp
On 16/09/14 22:51, Rasmus Villemoes wrote: > The kernel used to contain two functions for length-delimited, > case-insensitive string comparison, strnicmp with correct semantics > and a slightly buggy strncasecmp. The latter is the POSIX name, so > strnicmp was renamed to strncasecmp, and strnicmp made into a wrapper > for the new strncasecmp to avoid breaking existing users. > > To allow the compat wrapper strnicmp to be removed at some point in > the future, and to avoid the extra indirection cost, do > s/strnicmp/strncasecmp/g. > > Cc: Marek Lindner > Cc: b.a.t.m@lists.open-mesh.org > Cc: net...@vger.kernel.org > Signed-off-by: Rasmus Villemoes Acked-by: Antonio Quartulli Thank you -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH] Drivers: net: wireless: mac80211_hwim fixed coding style issues
On 15/02/14 19:13, Johannes Berg wrote: > On Sat, 2014-02-15 at 12:55 +0100, Justin van Wijngaarden wrote: > >> @@ -1,19 +1,17 @@ >> -/* >> - * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 >> +/* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 > > I don't see any point in this - lots of code exists both ways, that's > really just fluff. The difference isn't even interesting enough to be > worth fixing up. This is how David S. Miller wants multiline comments to start in files within the networking subsystem..but I know you don't like it ;) (I just wanted to justify Justin). Cheers, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH] batman-adv: Use kasprintf
Hi all, On 28/06/14 21:13, Joe Perches wrote: > diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c > index f40cb04..d6fba94 100644 > --- a/net/batman-adv/sysfs.c > +++ b/net/batman-adv/sysfs.c > @@ -896,7 +896,7 @@ int batadv_throw_uevent(struct batadv_priv *bat_priv, > enum batadv_uev_type type, > { > int ret = -ENOMEM; > struct kobject *bat_kobj; > - char *uevent_env[4] = { NULL, NULL, NULL, NULL }; > + char *uevent_env[3]; Joe, why are you shortening this? kobject_uevent_env() expect a NULL-terminating array (that is the forth cell). ... > > ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env); And how is this change reducing the code space? For what concerns the labels, we use this pattern mostly all over the code: one single label/exit-point with the related NULL checks. Do you think that we can improve something by changing this? (I am not talking about the fastpath here). Cheers, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [RFC PATCH] net: core: move core networking work to power efficient workqueue
On 31/01/14 01:33, Zoran Markovic wrote: > From: Shaibal Dutta [...] > - schedule_delayed_work(&linkwatch_work, delay); > + queue_delayed_work(system_power_efficient_wq, > + &linkwatch_work, delay); before talking about technical details, here and in other spots of this patch the alignment is wrong. I think checkpatch should have said something about it. The first parameter on the new line should be aligned up to the column after the opening parenthesis. Regards, > } > > > diff --git a/net/core/netpoll.c b/net/core/netpoll.c > index c03f3de..2c8f839 100644 > --- a/net/core/netpoll.c > +++ b/net/core/netpoll.c > @@ -101,7 +101,8 @@ static void queue_process(struct work_struct *work) > __netif_tx_unlock(txq); > local_irq_restore(flags); > > - schedule_delayed_work(&npinfo->tx_work, HZ/10); > + queue_delayed_work(system_power_efficient_wq, > + &npinfo->tx_work, HZ/10); > return; > } > __netif_tx_unlock(txq); > @@ -423,7 +424,8 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct > sk_buff *skb, > > if (status != NETDEV_TX_OK) { > skb_queue_tail(&npinfo->txq, skb); > - schedule_delayed_work(&npinfo->tx_work,0); > + queue_delayed_work(system_power_efficient_wq, > + &npinfo->tx_work, 0); > } > } > EXPORT_SYMBOL(netpoll_send_skb_on_dev); > -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [B.A.T.M.A.N.] [PATCH -next 2/3] batman-adv: Use seq_overflow
Joe, we have other places in the batman-adv code where we use seq_printf, but at the moment we don't check the return value and we always return 0 at the end of the function. I think we could use seq_overflow here as well? Thanks, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH -next 2/3] batman-adv: Use seq_overflow
On 11/12/13 06:12, Joe Perches wrote: > Convert the uses of the return of seq_printf to > instead check seq_overflow to determine if a buffer > overflow has occurred. > > This will eventually allow seq_printf & seq_puts to > be converted to a void return instead of the often > misused return that is often assumed to be an int for > the number of bytes emitted ala printk. > > Signed-off-by: Joe Perches I assume this patch is going to be merged with the others in some tree. In that case: Acked-by: Antonio Quartulli Thanks, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH -next] batadv: Slight optimization of batadv_compare_eth
-BEGIN PGP SIGNED MESSAGE- Hash: SHA256 On 06/12/13 09:18, Joe Perches wrote: > Use CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS to check if this > function can be optimized by using the generic ether_addr_equal. > > Remove the unnecessary ?: after the unoptimized memcmp. > > Signed-off-by: Joe Perches Acked-by: Antonio Quartulli - -- Antonio Quartulli -BEGIN PGP SIGNATURE- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAEBCAAGBQJSoaKXAAoJEEKTMo6mOh1VMjoP/3GqGy0TQG1yXRzbrzAbbklJ 8Oa6DHQd2pSN/cJkR501JYwSUnCGh3m7eh7qc1HZYrEIgxIrJDLHWSqBL3FPtgap gQ92BBQG/pd38ZmmWSeZob44bEMEGAn00B1kgFx+Xb4YW4bP2rDAlvPxjjaqjT3U tBNbNYxUTBY2JMftI7WwEOwMWgLPVoHR1PhAbBTpbXMXCRrGp/vXN1xXam1xIDNx kahFmHA1Dc91dwr8ev4bcUjfGO2tXkQOEoqKRkwc1SEzuKTjgJNDxdbFNqG6vMAm k/aXhMvf3DOUkaCeslRQIkwefbKmzN69c1x2ND7S8mLEMIKyBuhKUFcf6PebD9SK Dc+KdwIx0lsaS2vGqOt6mUSie1KfY8QNAc74eF6XjDhKyJynYXe6S3L40uT4eLXR 8fwGClJmyYVIiYD65NOtiIx6C2uHFchjDpXjlz0X8N9oQ2Azr4e2RMTGkkoihvOh 94r1dmAaBrnRiAu6AkImhgN+lWgLglHWD5trmmRCxNEJf0iW+x9ZaR4yAQFDUP3k mrk5AN8JuRZkOeY5QK4AIZ0bFUh8tKW6JaNb8VHQGsLqjhAB/HPS6VljyRgMalyQ c/kLlEOIIZ9dsjMwKMgyoJ9bMtTYUTxrriKz12mQbx8mWvm4CMFEQglljQiuTyvh J+UsjAHs/ffW59JIGCfB =8xL7 -END PGP SIGNATURE- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH 09/21] batman-adv: slight optimization of addr compare
On 23/12/13 06:10, Ding Tianhong wrote: [...] > --- a/net/batman-adv/originator.c > +++ b/net/batman-adv/originator.c > @@ -41,7 +41,7 @@ int batadv_compare_orig(const struct hlist_node *node, > const void *data2) > const void *data1 = container_of(node, struct batadv_orig_node, >hash_entry); > > - return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); > + return ether_addr_equal_unaligned(data1, data2) ? 1 : 0; ether_addr_equal_unaligned() returns a bool value which is implicitly converted to 1 or 0: there is no need for the ternary if anymore. > } > > /** > diff --git a/net/batman-adv/translation-table.c > b/net/batman-adv/translation-table.c > index 4add57d..5e66d4b 100644 > --- a/net/batman-adv/translation-table.c > +++ b/net/batman-adv/translation-table.c > @@ -51,7 +51,7 @@ static int batadv_compare_tt(const struct hlist_node *node, > const void *data2) > const void *data1 = container_of(node, struct batadv_tt_common_entry, >hash_entry); > > - return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); > + return ether_addr_equal_unaligned(data1, data2) ? 1 : 0; same here Moreover, include linux/etherdevice.h in both files as explained in point 1) of Documentation/SubmitChecklist Thanks. Cheers, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH 09/21] batman-adv: slight optimization of addr compare
On 23/12/13 09:59, Joe Perches wrote: > On Mon, 2013-12-23 at 09:46 +0100, Antonio Quartulli wrote: >> On 23/12/13 06:10, Ding Tianhong wrote: >> >> [...] >> >>> --- a/net/batman-adv/originator.c >>> +++ b/net/batman-adv/originator.c >>> @@ -41,7 +41,7 @@ int batadv_compare_orig(const struct hlist_node *node, >>> const void *data2) >>> const void *data1 = container_of(node, struct batadv_orig_node, >>> hash_entry); >>> >>> - return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); >>> + return ether_addr_equal_unaligned(data1, data2) ? 1 : 0; >> >> ether_addr_equal_unaligned() returns a bool value which is implicitly >> converted to 1 or 0: there is no need for the ternary if anymore. > > Should these use batadv_compare_eth? > That makes sense. I was wondering whether we should get rid of batadv_compare_eth() at all and always use ether_addr_equal_unaligned(). The "unaligned explanation" is part of the name, so there is no need to use a commented helper anymore. However, until that moment it is better to get stuck to batadv_compare_eth(). Ding, can you also follow Joe's suggestion for this patch please? Thanks, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [B.A.T.M.A.N.] [PATCH v2 07/20] batman-adv: use batadv_compare_eth for concise
On 24/12/13 12:28, Ding Tianhong wrote: > It is better to use batadv_compate_eth instead of memcpy for > concise style. > > Cc: Marek Lindner > Cc: Simon Wunderlich > Cc: Antonio Quartulli > Cc: "David S. Miller" > Cc: b.a.t.m@lists.open-mesh.org > Cc: net...@vger.kernel.org > Cc: linux-kernel@vger.kernel.org > Signed-off-by: Tan Xiaojun > Signed-off-by: Ding Tianhong Acked-by: Antonio Quartulli -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [B.A.T.M.A.N.] [PATCH 3/3] batman-adv: Less function calls in batadv_is_ap_isolated() after error detection
On 04/11/15 04:56, SF Markus Elfring wrote: > From: Markus Elfring > Date: Tue, 3 Nov 2015 21:10:51 +0100 > > The variables "tt_local_entry" and "tt_global_entry" were eventually checked > again despite of a corresponding null pointer test before. > Let us avoid this double check by reordering a function call sequence > and the better selection of jump targets. > > Signed-off-by: Markus Elfring > --- > net/batman-adv/translation-table.c | 21 + > 1 file changed, 9 insertions(+), 12 deletions(-) > > diff --git a/net/batman-adv/translation-table.c > b/net/batman-adv/translation-table.c > index 965a004..3ac32d9 100644 > --- a/net/batman-adv/translation-table.c > +++ b/net/batman-adv/translation-table.c > @@ -3323,27 +3323,24 @@ bool batadv_is_ap_isolated(struct batadv_priv > *bat_priv, u8 *src, u8 *dst, > return false; > > if (!atomic_read(&vlan->ap_isolation)) > - goto out; > + goto vlan_free; > > tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid); > if (!tt_local_entry) > - goto out; > + goto vlan_free; > > tt_global_entry = batadv_tt_global_hash_find(bat_priv, src, vid); > if (!tt_global_entry) > - goto out; > + goto local_entry_free; > > - if (!_batadv_is_ap_isolated(tt_local_entry, tt_global_entry)) > - goto out; > - > - ret = true; > + if (_batadv_is_ap_isolated(tt_local_entry, tt_global_entry)) > + ret = true; > > -out: > + batadv_tt_global_entry_free_ref(tt_global_entry); > +local_entry_free: > + batadv_tt_local_entry_free_ref(tt_local_entry); > +vlan_free: > batadv_softif_vlan_free_ref(vlan); > - if (tt_global_entry) > - batadv_tt_global_entry_free_ref(tt_global_entry); > - if (tt_local_entry) > - batadv_tt_local_entry_free_ref(tt_local_entry); > return ret; Markus, if you really want to make this codestyle change, I'd suggest you to go through the whole batman-adv code and apply the same change where needed. It does not make sense to change the codestyle in one spot only. On top of that, by going through the batman-adv code you might agree that the current style is actually not a bad idea. Cheers, -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH] batman-adv: Remove uses of return value of seq_printf
Hi Joe, thank you very much for this patch. On 17/02/15 02:31, Joe Perches wrote: > This function is soon going to return void so remove the > return value use. > > Convert the return value to test seq_has_overflowed() instead. > > Signed-off-by: Joe Perches Acked-by: Antonio Quartulli -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH] batman-adv: Deinline batadv_orig_hash_find, save 9024 bytes
On Mon, Apr 25, 2016 at 03:25:22PM +0200, Denys Vlasenko wrote: > This function compiles to 473 bytes of machine code. > 21 callsites. > > text data bss dec hex filename > 95903266 20860288 35991552 152755106 91adba2 vmlinux_before > 95894242 20860288 35991552 152746082 91ab862 vmlinux Hi Danys, thanks for your patch. This function is used in a several performance critical code paths (i.e. packet forwarding). Are we sure we are not losing in performance here? Cheers, -- Antonio Quartulli signature.asc Description: Digital signature
Re: [PATCH] batman-adv: Deinline batadv_orig_hash_find, save 9024 bytes
On Mon, Apr 25, 2016 at 03:45:20PM +0200, Denys Vlasenko wrote: > On 04/25/2016 03:39 PM, Antonio Quartulli wrote: > > On Mon, Apr 25, 2016 at 03:25:22PM +0200, Denys Vlasenko wrote: > >> This function compiles to 473 bytes of machine code. > >> 21 callsites. > >> > >> text data bss dec hex filename > >> 95903266 20860288 35991552 152755106 91adba2 vmlinux_before > >> 95894242 20860288 35991552 152746082 91ab862 vmlinux > > > > Hi Danys, > > > > thanks for your patch. This function is used in a several performance > > critical > > code paths (i.e. packet forwarding). > > > > Are we sure we are not losing in performance here? > > Is this a common case? > > if (!hash) > return NULL; > > If yes, then we can inline this part only. Unfortunately not: this case is rather rare at runtime. These hash tables are initialized when the batman virtual interface is created and should be freed only upon interface shutdown. (actually I believe this might be a good candidate for an unlikely()) Cheers, -- Antonio Quartulli signature.asc Description: Digital signature
Re: [PATCH] batman-adv: Less function calls in batadv_is_ap_isolated() after error detection
On Mon, Mar 14, 2016 at 03:25:02PM -0400, David Miller wrote: > From: SF Markus Elfring > Date: Fri, 11 Mar 2016 13:40:56 +0100 > > > From: Markus Elfring > > Date: Fri, 11 Mar 2016 13:10:20 +0100 > > > > The variables "tt_local_entry" and "tt_global_entry" were eventually > > checked again despite of a corresponding null pointer test before. > > > > * Avoid this double check by reordering a function call sequence > > and the better selection of jump targets. > > > > * Omit the initialisation for these variables at the beginning then. > > > > Signed-off-by: Markus Elfring > > I am assuming Antonio will take this in via his tree. > Yeah, it will go through our tree. Still under review right now. Cheers, -- Antonio Quartulli signature.asc Description: Digital signature
Re: [PATCH] batman-adv: clarify CFG80211 dependency
On Wed, Mar 02, 2016 at 02:54:35PM +0100, Arnd Bergmann wrote: > The driver calls cfg80211_get_station, which may be part of a > module, so we must not enable BATMAN_ADV_BATMAN_V if > BATMAN_ADV=y and CFG80211=m: > > net/built-in.o: In function `batadv_v_elp_get_throughput': > (text+0x5c62c): undefined reference to `cfg80211_get_station' > > This clarifies the dependency to cover all combinations. > > Signed-off-by: Arnd Bergmann > Fixes: c833484e5f38 ("batman-adv: ELP - compute the metric based on the > estimated throughput") Acked-by: Antonio Quartulli Thanks a lot Arnd! David, could you please merge this change in net-next directly ? Regards, -- Antonio Quartulli signature.asc Description: Digital signature
Re: [PATCH 07/14] batman-adv: use list_for_each_entry_safe
Hi Geliang, > static int batadv_socket_release(struct inode *inode, struct file *file) > { > - struct batadv_socket_client *socket_client = file->private_data; > - struct batadv_socket_packet *socket_packet; > - struct list_head *list_pos, *list_pos_tmp; > + struct batadv_socket_client *client = file->private_data; > + struct batadv_socket_packet *packet, *tmp; > [...] > + list_for_each_entry_safe(packet, tmp, &client->queue_list, list) { I guess you renamed those variables to make sure that the statement above would fit in 80 chars.. in that case the patch looks good. Acked-by: Antonio Quartulli -- Antonio Quartulli signature.asc Description: OpenPGP digital signature
Re: [PATCH net-next v8 03/24] ovpn: add basic netlink support
On 04/10/2024 18:13, Donald Hunter wrote: On Wed, 2 Oct 2024 at 10:03, Antonio Quartulli wrote: +definitions: + - +type: const +name: nonce-tail-size +value: 8 + - +type: enum +name: cipher-alg +value-start: 0 value-start defaults to 0 for enum so this is unnecessary. Same for the following enum definitions. ACK +entries: [ none, aes-gcm, chacha20-poly1305 ] + - +type: enum +name: del-peer-reason +value-start: 0 +entries: [ teardown, userspace, expired, transport-error, transport-disconnect ] + - +type: enum +name: key-slot +value-start: 0 +entries: [ primary, secondary ] + - +type: enum +name: mode +value-start: 0 +entries: [ p2p, mp ] + [...] +operations: + list: +- + name: dev-new + attribute-set: ovpn + flags: [ admin-perm ] + doc: Create a new interface of type ovpn + do: +request: + attributes: +- ifname +- mode +reply: + attributes: +- ifname +- ifindex +- + name: dev-del + attribute-set: ovpn + flags: [ admin-perm ] + doc: Delete existing interface of type ovpn + do: +pre: ovpn-nl-pre-doit +post: ovpn-nl-post-doit +request: + attributes: +- ifindex There's no dev-get do/dump op. I think there should be one for diagnostics and metrics. I am not sure how much information it can provide (as of now we only have the 'mode' that is being set upon creation). In any case, I am not against implementing the op now and extend it later as we see fit. +- + name: key-new + attribute-set: ovpn + flags: [ admin-perm ] + doc: Add a cipher key for a specific peer + do: +pre: ovpn-nl-pre-doit +post: ovpn-nl-post-doit +request: + attributes: +- ifindex +- keyconf +- + name: key-swap + attribute-set: ovpn + flags: [ admin-perm ] + doc: Swap primary and secondary session keys for a specific peer + do: +pre: ovpn-nl-pre-doit +post: ovpn-nl-post-doit +request: + attributes: +- ifindex +- keyconf +- + name: key-swap-ntf + notify: key-new This doesn't work because key-new doesn't have a reply. You should define it with an event: block instead. You can see the build errors here: make -C tools/net/ynl Oh, I wasn't aware of this subfolder. Thanks for pointing it out! I am thinking that it may make sense to implement a key-get op to extract non-sensible data about the keys (i.e. what cipher was configured). This may be useful for debugging as well. At that point the key-swap-ntf can re-use the key-get as notify. Cheers, CC ovpn-user.o In file included from ovpn-user.c:8: ovpn-user.h:1194:33: error: field ‘obj’ has incomplete type 1194 | struct ovpn_key_new_rsp obj __attribute__((aligned(8))); | ^~~ ovpn-user.c:835:35: error: ‘ovpn_key_new_rsp_parse’ undeclared here (not in a function); did you mean ‘ovpn_dev_new_rsp_parse’? 835 | .cb = ovpn_key_new_rsp_parse, | ^~ | ovpn_dev_new_rsp_parse make[1]: *** [Makefile:41: ovpn-user.o] Error 1 + doc: | +Notification about key having exhausted its IV space and requiring +renegotiation + mcgrp: peers +- + name: key-del + attribute-set: ovpn + flags: [ admin-perm ] + doc: Delete cipher key for a specific peer + do: +pre: ovpn-nl-pre-doit +post: ovpn-nl-post-doit +request: + attributes: +- ifindex +- keyconf + +mcast-groups: + list: +- + name: peers -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v8 01/24] netlink: add NLA_POLICY_MAX_LEN macro
On 07/10/24 17:53, Jakub Kicinski wrote: On Mon, 7 Oct 2024 12:04:22 +0200 Antonio Quartulli wrote: Or we could check if len(self.checks) <= 1 early and throw our hands up if there is more, for now? We already perform the same check in the 'else' branch below. It'd be about moving it at the beginning of the function and bail out if true, right? Should I modify this patch and move the check above? I just sent the refactor patch, that seemed easier than explaining ;) Great, thanks :-) -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v8 03/24] ovpn: add basic netlink support
Hi, On 07/10/24 17:32, Jiri Pirko wrote: Wed, Oct 02, 2024 at 11:02:17AM CEST, anto...@openvpn.net wrote: [...] +operations: + list: +- + name: dev-new + attribute-set: ovpn + flags: [ admin-perm ] + doc: Create a new interface of type ovpn + do: +request: + attributes: +- ifname +- mode +reply: + attributes: +- ifname +- ifindex +- + name: dev-del Why you expose new and del here in ovn specific generic netlink iface? Why can't you use the exising RTNL api which is used for creation and destruction of other types of devices? That was my original approach in v1, but it was argued that an ovpn interface needs a userspace program to be configured and used in a meaningful way, therefore it was decided to concentrate all iface mgmt APIs along with the others in the netlink family and to not expose any RTNL ops. However, recently we decided to add a dellink implementation for better integration with network namespaces and to allow the user to wipe a dangling interface. In the future we are planning to also add the possibility to create a "persistent interface", that is an interface created before launching any userspace program and that survives when the latter is stopped. I can guess this functionality may be better suited for RTNL, but I am not sure yet. @Jiri: do you have any particular opinion why we should use RTNL ops and not netlink for creating/destroying interfaces? I feel this is mostly a matter of taste, but maybe there are technical reasons we should consider. Thanks a lot for your contribution. Regards, ip link add [link DEV | parentdev NAME] [ name ] NAME [ txqueuelen PACKETS ] [ address LLADDR ] [ broadcast LLADDR ] [ mtu MTU ] [index IDX ] [ numtxqueues QUEUE_COUNT ] [ numrxqueues QUEUE_COUNT ] [ netns { PID | NETNSNAME | NETNSFILE } ] type TYPE [ ARGS ] ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ] Lots of examples of existing types creation is for example here: https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking + attribute-set: ovpn + flags: [ admin-perm ] + doc: Delete existing interface of type ovpn + do: +pre: ovpn-nl-pre-doit +post: ovpn-nl-post-doit +request: + attributes: +- ifindex [...] -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v8 03/24] ovpn: add basic netlink support
On 08/10/2024 14:52, Jiri Pirko wrote: Tue, Oct 08, 2024 at 11:16:01AM CEST, anto...@openvpn.net wrote: On 08/10/2024 10:58, Jiri Pirko wrote: Tue, Oct 08, 2024 at 10:01:40AM CEST, anto...@openvpn.net wrote: Hi, On 07/10/24 17:32, Jiri Pirko wrote: Wed, Oct 02, 2024 at 11:02:17AM CEST, anto...@openvpn.net wrote: [...] +operations: + list: +- + name: dev-new + attribute-set: ovpn + flags: [ admin-perm ] + doc: Create a new interface of type ovpn + do: +request: + attributes: +- ifname +- mode +reply: + attributes: +- ifname +- ifindex +- + name: dev-del Why you expose new and del here in ovn specific generic netlink iface? Why can't you use the exising RTNL api which is used for creation and destruction of other types of devices? That was my original approach in v1, but it was argued that an ovpn interface needs a userspace program to be configured and used in a meaningful way, therefore it was decided to concentrate all iface mgmt APIs along with the others in the netlink family and to not expose any RTNL ops. Can you please point me to the message id? from Sergey and subsequent replies. RTNL vs NL topic starts right after the definition of 'ovpn_link_ops' Yeah, does not make sense to me. All devices should implement common rtnl ops, the extra-config, if needed, could be on a separate channel. I don't find Sergey's argumentation valid. Thanks a lot for taking the time to read our conversation. Ok, considering all points we have discussed so far (including future developments already on the roadmap), I think it makes sense to go back to RTNL and drop the new/del-dev ops from the netlink family. Will do that in v9. Regards, Recently Kuniyuki commented on this topic as well in: <20240919055259.17622-1-kun...@amazon.com> and that is why I added a default dellink implemetation. Having dellink without newlink implemented is just wrong. However, recently we decided to add a dellink implementation for better integration with network namespaces and to allow the user to wipe a dangling interface. Hmm, one more argument to have symmetric add/del impletentation in RTNL In the future we are planning to also add the possibility to create a "persistent interface", that is an interface created before launching any userspace program and that survives when the latter is stopped. I can guess this functionality may be better suited for RTNL, but I am not sure yet. That would be quite confusing to have RTNL and genetlink iface to add/del device. From what you described above, makes more sent to have it just in RTNL All in all I tend to agree. @Jiri: do you have any particular opinion why we should use RTNL ops and not netlink for creating/destroying interfaces? I feel this is mostly a matter of taste, but maybe there are technical reasons we should consider. Well. technically, you can probabaly do both. But it is quite common that you can add/delete these kind of devices over RTNL. Lots of examples. People are used to it, aligns with existing flows. The only counterargument I see is the one brought by Sergey: "the ovpn interface is not usable after creation, if no openvpn process is running". However, allowing to create "persistent interfaces" will define a use-case for having an ovpn device without any userspace process. @Sergey what is your opinion here? I am not sure persistent interfaces were discussed at the time you brought your point about RTNL vs NL. Regards, Thanks a lot for your contribution. Regards, ip link add [link DEV | parentdev NAME] [ name ] NAME [ txqueuelen PACKETS ] [ address LLADDR ] [ broadcast LLADDR ] [ mtu MTU ] [index IDX ] [ numtxqueues QUEUE_COUNT ] [ numrxqueues QUEUE_COUNT ] [ netns { PID | NETNSNAME | NETNSFILE } ] type TYPE [ ARGS ] ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ] Lots of examples of existing types creation is for example here: https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking + attribute-set: ovpn + flags: [ admin-perm ] + doc: Delete existing interface of type ovpn + do: +pre: ovpn-nl-pre-doit +post: ovpn-nl-post-doit +request: + attributes: + - ifindex [...] -- Antonio Quartulli OpenVPN Inc. -- Antonio Quartulli OpenVPN Inc. -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v8 01/24] netlink: add NLA_POLICY_MAX_LEN macro
Hi, On 04/10/2024 15:38, Jakub Kicinski wrote: On Fri, 04 Oct 2024 13:58:04 +0100 Donald Hunter wrote: @@ -466,6 +466,8 @@ class TypeBinary(Type): def _attr_policy(self, policy): if 'exact-len' in self.checks: mem = 'NLA_POLICY_EXACT_LEN(' + str(self.get_limit('exact-len')) + ')' +elif 'max-len' in self.checks: +mem = 'NLA_POLICY_MAX_LEN(' + str(self.get_limit('max-len')) + ')' This takes precedence over min-length. What if both are set? The logic should probably check and use NLA_POLICY_RANGE Or we could check if len(self.checks) <= 1 early and throw our hands up if there is more, for now? We already perform the same check in the 'else' branch below. It'd be about moving it at the beginning of the function and bail out if true, right? Should I modify this patch and move the check above? Cheers, else: mem = '{ ' if len(self.checks) == 1 and 'min-len' in self.checks: Perhaps this should use NLA_POLICY_MIN_LEN ? In fact the current code looks broken to me because the NLA_BINARY len check in validate_nla() is a max length check, right? https://elixir.bootlin.com/linux/v6.11.1/source/lib/nlattr.c#L499 The alternative is you emit an explicit initializer that includes the correct NLA_VALIDATE_* type and sets type, min and/or max. Yeah, this code leads to endless confusion. We use NLA_UNSPEC (0) if min-len is set (IOW we don't set .type to NLA_BINARY). NLA_UNSPEC has different semantics for len. Agreed that we should probably clean this up, but no bug AFAICT. -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v8 24/24] testing/selftest: add test tool and scripts for ovpn module
Hi, On 03/10/2024 00:35, Shuah Khan wrote: On 10/2/24 03:02, Antonio Quartulli wrote: The ovpn-cli tool can be compiled and used as selftest for the ovpn kernel module. Does this test load ovpn module before running tests? If so does it unload the modules after tests are complete? The module is self loaded upon first netlink API call. Therefore I presume I should take care of unloading too. It implementes the netlink API and can thus be integrated in any Spelling - implements ACK script for more automated testing. Along with the tool, 2 scripts are added that perform basic functionality tests by means of network namespaces. The scripts can be performed in sequence by running run.sh Cc: sh...@kernel.org Cc: linux-kselft...@vger.kernel.org Signed-off-by: Antonio Quartulli --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net/ovpn/.gitignore | 2 + tools/testing/selftests/net/ovpn/Makefile | 18 + tools/testing/selftests/net/ovpn/config | 8 + tools/testing/selftests/net/ovpn/data-test-tcp.sh | 9 + tools/testing/selftests/net/ovpn/data-test.sh | 153 ++ tools/testing/selftests/net/ovpn/data64.key | 5 + tools/testing/selftests/net/ovpn/float-test.sh | 118 ++ tools/testing/selftests/net/ovpn/ovpn-cli.c | 1822 + tools/testing/selftests/net/ovpn/tcp_peers.txt | 5 + tools/testing/selftests/net/ovpn/udp_peers.txt | 5 + 12 files changed, 2147 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f753060d4e2467a786778ddd4f835861a603ce02..ffd997cc6a1f1fbc5bc954b585bc15ef7bf2486a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17457,6 +17457,7 @@ T: git https://github.com/OpenVPN/linux-kernel-ovpn.git F: drivers/net/ovpn/ F: include/uapi/linux/ovpn.h F: Documentation/netlink/spec/ovpn.yaml +F: tools/testing/selftests/net/ovpn/ P54 WIRELESS DRIVER M: Christian Lamparter diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index b38199965f99014f3e2636fe8d705972f2c0d148..3ae2dd6492ca70d5e317c6e5b4e2560b060e3214 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -68,6 +68,7 @@ TARGETS += net/hsr TARGETS += net/mptcp TARGETS += net/netfilter TARGETS += net/openvswitch +TARGETS += net/ovpn TARGETS += net/packetdrill TARGETS += net/rds TARGETS += net/tcp_ao diff --git a/tools/testing/selftests/net/ovpn/.gitignore b/tools/testing/selftests/net/ovpn/.gitignore new file mode 100644 index ..ee44c081ca7c089933659689303c303a9fa9713b --- /dev/null +++ b/tools/testing/selftests/net/ovpn/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0+ +ovpn-cli diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile new file mode 100644 index ..65e23eb0ba86d31aa365b08a8c3468dc56a0d1a4 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020-2024 OpenVPN, Inc. +# +CFLAGS = -Wall -I../../../../../usr/include +CFLAGS += $(shell pkg-config --cflags libnl-3.0 libnl-genl-3.0) + +LDFLAGS = -lmbedtls -lmbedcrypto +LDFLAGS += $(shell pkg-config --libs libnl-3.0 libnl-genl-3.0) + +ovpn-cli: ovpn-cli.c + $(CC) -o ovpn-cli ovpn-cli.c $(CFLAGS) $(LDFLAGS) + +TEST_PROGS = data-test.sh \ + data-test-tcp.sh \ + float-test.sh +TEST_GEN_FILES = ovpn-cli Can you make sure "make kselftest-install" installs all of the scripts and executables necessary to run this test? ok, will do! Thanks for your feedback! Regards, thanks, -- Shuah -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v9 04/23] ovpn: add basic interface creation/destruction/management routines
On 16/10/2024 10:27, Jiri Pirko wrote: Wed, Oct 16, 2024 at 03:03:04AM CEST, anto...@openvpn.net wrote: Add basic infrastructure for handling ovpn interfaces. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 115 -- drivers/net/ovpn/main.h | 7 +++ drivers/net/ovpn/ovpnstruct.h | 8 +++ drivers/net/ovpn/packet.h | 40 +++ include/uapi/linux/if_link.h | 15 ++ 5 files changed, 180 insertions(+), 5 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index d5bdb0055f4dd3a6e32dc6e792bed1e7fd59e101..eead7677b8239eb3c48bb26ca95492d88512b8d4 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -10,18 +10,52 @@ #include #include #include +#include +#include #include -#include +#include #include "ovpnstruct.h" #include "main.h" #include "netlink.h" #include "io.h" +#include "packet.h" /* Driver info */ #define DRV_DESCRIPTION "OpenVPN data channel offload (ovpn)" #define DRV_COPYRIGHT "(C) 2020-2024 OpenVPN, Inc." +static void ovpn_struct_free(struct net_device *net) +{ +} + +static int ovpn_net_open(struct net_device *dev) +{ + netif_tx_start_all_queues(dev); + return 0; +} + +static int ovpn_net_stop(struct net_device *dev) +{ + netif_tx_stop_all_queues(dev); + return 0; +} + +static const struct net_device_ops ovpn_netdev_ops = { + .ndo_open = ovpn_net_open, + .ndo_stop = ovpn_net_stop, + .ndo_start_xmit = ovpn_net_xmit, +}; + +static const struct device_type ovpn_type = { + .name = OVPN_FAMILY_NAME, +}; + +static const struct nla_policy ovpn_policy[IFLA_OVPN_MAX + 1] = { + [IFLA_OVPN_MODE] = NLA_POLICY_RANGE(NLA_U8, OVPN_MODE_P2P, + OVPN_MODE_MP), +}; + /** * ovpn_dev_is_valid - check if the netdevice is of type 'ovpn' * @dev: the interface to check @@ -33,16 +67,76 @@ bool ovpn_dev_is_valid(const struct net_device *dev) return dev->netdev_ops->ndo_start_xmit == ovpn_net_xmit; } +static void ovpn_setup(struct net_device *dev) +{ + /* compute the overhead considering AEAD encryption */ + const int overhead = sizeof(u32) + NONCE_WIRE_SIZE + 16 + +sizeof(struct udphdr) + +max(sizeof(struct ipv6hdr), sizeof(struct iphdr)); + + netdev_features_t feat = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM | +NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | +NETIF_F_HIGHDMA; + + dev->needs_free_netdev = true; + + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + + dev->netdev_ops = &ovpn_netdev_ops; + + dev->priv_destructor = ovpn_struct_free; + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->mtu = ETH_DATA_LEN - overhead; + dev->min_mtu = IPV4_MIN_MTU; + dev->max_mtu = IP_MAX_MTU - overhead; + + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->priv_flags |= IFF_NO_QUEUE; + + dev->lltx = true; + dev->features |= feat; + dev->hw_features |= feat; + dev->hw_enc_features |= feat; + + dev->needed_headroom = OVPN_HEAD_ROOM; + dev->needed_tailroom = OVPN_MAX_PADDING; + + SET_NETDEV_DEVTYPE(dev, &ovpn_type); +} + static int ovpn_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - return -EOPNOTSUPP; + struct ovpn_struct *ovpn = netdev_priv(dev); + enum ovpn_mode mode = OVPN_MODE_P2P; + + if (data && data[IFLA_OVPN_MODE]) { + mode = nla_get_u8(data[IFLA_OVPN_MODE]); Some sanity check perhaps? "validate" op is here for that purpose. Isn't the parsing happening here enough https://elixir.bootlin.com/linux/v6.12-rc3/source/net/core/rtnetlink.c#L3659 The IFINFO_DATA is parsed using the policy I provided (which comes with limits for the mode attribute). Or am I misreading the code and I still need to provide an implementation for .validate? Regards, + netdev_dbg(dev, "setting device mode: %u\n", mode); + } + + ovpn->dev = dev; + ovpn->mode = mode; + + /* turn carrier explicitly off after registration, this way state is +* clearly defined +*/ + netif_carrier_off(dev); + + return register_netdevice(dev); [...] -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v9 23/23] testing/selftest: add test tool and scripts for ovpn module
On 16/10/2024 23:14, Shuah Khan wrote: On 10/15/24 19:03, Antonio Quartulli wrote: The ovpn-cli tool can be compiled and used as selftest for the ovpn kernel module. It implements the netlink API and can thus be integrated in any script for more automated testing. Along with the tool, 2 scripts are added that perform basic functionality tests by means of network namespaces. The scripts can be performed in sequence by running run.sh Cc: sh...@kernel.org Cc: linux-kselft...@vger.kernel.org Signed-off-by: Antonio Quartulli I almost gave my Reviewed-by when I saw the very long argument parsing in the main() - please see comment below under main(). Let's simply the logic using getopt() - it is way too long and complex. [...] This is lng arguments parsing. What's the reason to not use getopt() Doesn't it simplify all ofthie logic? I would like to see it simplified for maintainability. This tool was originally very simple...then...this happened :-D I agree getopt() could help making this whole function easier to read and maintain. I will include this change in v10. Thanks! Regards, -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v9 23/23] testing/selftest: add test tool and scripts for ovpn module
On 16/10/2024 23:14, Shuah Khan wrote: On 10/15/24 19:03, Antonio Quartulli wrote: The ovpn-cli tool can be compiled and used as selftest for the ovpn kernel module. It implements the netlink API and can thus be integrated in any script for more automated testing. Along with the tool, 2 scripts are added that perform basic functionality tests by means of network namespaces. The scripts can be performed in sequence by running run.sh Cc: sh...@kernel.org Cc: linux-kselft...@vger.kernel.org Signed-off-by: Antonio Quartulli I almost gave my Reviewed-by when I saw the very long argument parsing in the main() - please see comment below under main(). Let's simply the logic using getopt() - it is way too long and complex. Shuan, while looking into this I got the feeling that getopt() may not be the right tool for this parser. The ovpn-cli tool doesn't truly excpect "options" with their arguments on the command line, but it rather takes a "command" followed by command-specific arguments/modifiers. More like the 'ip' tool (from iproute2). The large if/else block is checking for the specified command. Moreover commands are *mutually exclusive*. Converting this logic to getopt() seems quite complicated as I'd need to: * keep track of the first specified command (which may be in any position) * prevent other commands to be thrown on the command line * come up with an option for each command-specific argument (and make sure only those required by the specified command are present) Are you sure this is the right path to follow? The 'ip' tool also implements something similar after all. Thanks a lot. Regards, --- MAINTAINERS | 1 + tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net/ovpn/.gitignore | 2 + tools/testing/selftests/net/ovpn/Makefile | 16 + tools/testing/selftests/net/ovpn/config | 10 + tools/testing/selftests/net/ovpn/data-test-tcp.sh | 9 + tools/testing/selftests/net/ovpn/data-test.sh | 157 ++ tools/testing/selftests/net/ovpn/data64.key | 5 + tools/testing/selftests/net/ovpn/float-test.sh | 122 ++ tools/testing/selftests/net/ovpn/ovpn-cli.c | 2136 +++ ++ tools/testing/selftests/net/ovpn/tcp_peers.txt | 5 + tools/testing/selftests/net/ovpn/udp_peers.txt | 5 + 12 files changed, 2469 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8edccdabd96ab4a4e8e9ed24d18ecbcd6d33ecec..ee94f245a18557974ff35b82d9d6d883357e6a01 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17476,6 +17476,7 @@ T: git https://github.com/OpenVPN/linux- kernel-ovpn.git F: Documentation/netlink/specs/ovpn.yaml F: drivers/net/ovpn/ F: include/uapi/linux/ovpn.h +F: tools/testing/selftests/net/ovpn/ P54 WIRELESS DRIVER M: Christian Lamparter diff --git a/tools/testing/selftests/Makefile b/tools/testing/ selftests/Makefile index b38199965f99014f3e2636fe8d705972f2c0d148..3ae2dd6492ca70d5e317c6e5b4e2560b060e3214 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -68,6 +68,7 @@ TARGETS += net/hsr TARGETS += net/mptcp TARGETS += net/netfilter TARGETS += net/openvswitch +TARGETS += net/ovpn TARGETS += net/packetdrill TARGETS += net/rds TARGETS += net/tcp_ao diff --git a/tools/testing/selftests/net/ovpn/.gitignore b/tools/ testing/selftests/net/ovpn/.gitignore new file mode 100644 index ..ee44c081ca7c089933659689303c303a9fa9713b --- /dev/null +++ b/tools/testing/selftests/net/ovpn/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0+ +ovpn-cli diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/ testing/selftests/net/ovpn/Makefile new file mode 100644 index ..9510c9b171390809bcce9f8c81a3a0abce885ac0 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020-2024 OpenVPN, Inc. +# +CFLAGS = -Wall -Wl,--no-as-needed -g -O2 $(KHDR_INCLUDES) +CFLAGS += $(shell pkg-config --cflags libnl-3.0 libnl-genl-3.0) + +LDFLAGS = -lmbedtls -lmbedcrypto +LDFLAGS += $(shell pkg-config --libs libnl-3.0 libnl-genl-3.0) + +TEST_PROGS = data-test.sh \ + data-test-tcp.sh \ + float-test.sh + +TEST_GEN_FILES = ovpn-cli + +include ../../lib.mk diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/ selftests/net/ovpn/config new file mode 100644 index ..71946ba9fa175c191725e369eb9b973503d9d9c4 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/config @@ -0,0 +1,10 @@ +CONFIG_NET=y +CONFIG_INET=y +CONFIG_STREAM_PARSER=y +CONFIG_NET_UDP_TUNNEL=y +CONFIG_DST_CACHE=y +CONFIG_CRYPTO=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_OV
Re: [PATCH net-next v9 23/23] testing/selftest: add test tool and scripts for ovpn module
On 17/10/2024 23:40, Shuah Khan wrote: On 10/17/24 05:27, Antonio Quartulli wrote: On 16/10/2024 23:14, Shuah Khan wrote: On 10/15/24 19:03, Antonio Quartulli wrote: The ovpn-cli tool can be compiled and used as selftest for the ovpn kernel module. It implements the netlink API and can thus be integrated in any script for more automated testing. Along with the tool, 2 scripts are added that perform basic functionality tests by means of network namespaces. The scripts can be performed in sequence by running run.sh Cc: sh...@kernel.org Cc: linux-kselft...@vger.kernel.org Signed-off-by: Antonio Quartulli I almost gave my Reviewed-by when I saw the very long argument parsing in the main() - please see comment below under main(). Let's simply the logic using getopt() - it is way too long and complex. Shuan, while looking into this I got the feeling that getopt() may not be the right tool for this parser. The ovpn-cli tool doesn't truly excpect "options" with their arguments on the command line, but it rather takes a "command" followed by command-specific arguments/modifiers. More like the 'ip' tool (from iproute2). The large if/else block is checking for the specified command. Moreover commands are *mutually exclusive*. Converting this logic to getopt() seems quite complicated as I'd need to: * keep track of the first specified command (which may be in any position) * prevent other commands to be thrown on the command line * come up with an option for each command-specific argument (and make sure only those required by the specified command are present) Thank for looking into it. I would like to make a suggestion to add a parse() routine and move this logic there instead of making the main() very long. It will be easier to read the code as well. Ok, I get your point. Let me work something out :) Thanks again for your feedback! Regards, Are you sure this is the right path to follow? The 'ip' tool also implements something similar after all. Sometimes argument parsing takes on life as new options get added. It starts out as a couple if else conditionals and expands - when I see a long argument parsing code, I like to pause and ask the question. Sounds like you case is more complex. thanks, -- Shuah -- Antonio Quartulli OpenVPN Inc.
Re: [PATCH net-next v8 03/24] ovpn: add basic netlink support
On 08/10/2024 10:58, Jiri Pirko wrote: Tue, Oct 08, 2024 at 10:01:40AM CEST, anto...@openvpn.net wrote: Hi, On 07/10/24 17:32, Jiri Pirko wrote: Wed, Oct 02, 2024 at 11:02:17AM CEST, anto...@openvpn.net wrote: [...] +operations: + list: +- + name: dev-new + attribute-set: ovpn + flags: [ admin-perm ] + doc: Create a new interface of type ovpn + do: +request: + attributes: +- ifname +- mode +reply: + attributes: +- ifname +- ifindex +- + name: dev-del Why you expose new and del here in ovn specific generic netlink iface? Why can't you use the exising RTNL api which is used for creation and destruction of other types of devices? That was my original approach in v1, but it was argued that an ovpn interface needs a userspace program to be configured and used in a meaningful way, therefore it was decided to concentrate all iface mgmt APIs along with the others in the netlink family and to not expose any RTNL ops. Can you please point me to the message id? from Sergey and subsequent replies. RTNL vs NL topic starts right after the definition of 'ovpn_link_ops' Recently Kuniyuki commented on this topic as well in: <20240919055259.17622-1-kun...@amazon.com> and that is why I added a default dellink implemetation. However, recently we decided to add a dellink implementation for better integration with network namespaces and to allow the user to wipe a dangling interface. Hmm, one more argument to have symmetric add/del impletentation in RTNL In the future we are planning to also add the possibility to create a "persistent interface", that is an interface created before launching any userspace program and that survives when the latter is stopped. I can guess this functionality may be better suited for RTNL, but I am not sure yet. That would be quite confusing to have RTNL and genetlink iface to add/del device. From what you described above, makes more sent to have it just in RTNL All in all I tend to agree. @Jiri: do you have any particular opinion why we should use RTNL ops and not netlink for creating/destroying interfaces? I feel this is mostly a matter of taste, but maybe there are technical reasons we should consider. Well. technically, you can probabaly do both. But it is quite common that you can add/delete these kind of devices over RTNL. Lots of examples. People are used to it, aligns with existing flows. The only counterargument I see is the one brought by Sergey: "the ovpn interface is not usable after creation, if no openvpn process is running". However, allowing to create "persistent interfaces" will define a use-case for having an ovpn device without any userspace process. @Sergey what is your opinion here? I am not sure persistent interfaces were discussed at the time you brought your point about RTNL vs NL. Regards, Thanks a lot for your contribution. Regards, ip link add [link DEV | parentdev NAME] [ name ] NAME [ txqueuelen PACKETS ] [ address LLADDR ] [ broadcast LLADDR ] [ mtu MTU ] [index IDX ] [ numtxqueues QUEUE_COUNT ] [ numrxqueues QUEUE_COUNT ] [ netns { PID | NETNSNAME | NETNSFILE } ] type TYPE [ ARGS ] ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ] Lots of examples of existing types creation is for example here: https://developers.redhat.com/blog/2018/10/22/introduction-to-linux-interfaces-for-virtual-networking + attribute-set: ovpn + flags: [ admin-perm ] + doc: Delete existing interface of type ovpn + do: +pre: ovpn-nl-pre-doit +post: ovpn-nl-post-doit +request: + attributes: + - ifindex [...] -- Antonio Quartulli OpenVPN Inc. -- Antonio Quartulli OpenVPN Inc.
[PATCH net-next v8 03/24] ovpn: add basic netlink support
This commit introduces basic netlink support with family registration/unregistration functionalities and stub pre/post-doit. More importantly it introduces the YAML uAPI description along with its auto-generated files: - include/uapi/linux/ovpn.h - drivers/net/ovpn/netlink-gen.c - drivers/net/ovpn/netlink-gen.h Cc: donald.hun...@gmail.com Signed-off-by: Antonio Quartulli --- Documentation/netlink/specs/ovpn.yaml | 387 ++ MAINTAINERS | 2 + drivers/net/ovpn/Makefile | 2 + drivers/net/ovpn/main.c | 15 +- drivers/net/ovpn/netlink-gen.c| 224 drivers/net/ovpn/netlink-gen.h| 42 drivers/net/ovpn/netlink.c| 158 ++ drivers/net/ovpn/netlink.h| 15 ++ drivers/net/ovpn/ovpnstruct.h | 21 ++ include/uapi/linux/ovpn.h | 116 ++ 10 files changed, 981 insertions(+), 1 deletion(-) diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml new file mode 100644 index ..36983f293ecbd59532114808a20737005e91ad72 --- /dev/null +++ b/Documentation/netlink/specs/ovpn.yaml @@ -0,0 +1,387 @@ +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +# +# Author: Antonio Quartulli +# +# Copyright (c) 2024, OpenVPN Inc. +# + +name: ovpn + +protocol: genetlink + +doc: Netlink protocol to control OpenVPN network devices + +definitions: + - +type: const +name: nonce-tail-size +value: 8 + - +type: enum +name: cipher-alg +value-start: 0 +entries: [ none, aes-gcm, chacha20-poly1305 ] + - +type: enum +name: del-peer-reason +value-start: 0 +entries: [ teardown, userspace, expired, transport-error, transport-disconnect ] + - +type: enum +name: key-slot +value-start: 0 +entries: [ primary, secondary ] + - +type: enum +name: mode +value-start: 0 +entries: [ p2p, mp ] + +attribute-sets: + - +name: peer +attributes: + - +name: id +type: u32 +doc: | + The unique ID of the peer. To be used to identify peers during + operations +checks: + max: 0xFF + - +name: remote-ipv4 +type: u32 +doc: The remote IPv4 address of the peer +byte-order: big-endian +display-hint: ipv4 + - +name: remote-ipv6 +type: binary +doc: The remote IPv6 address of the peer +display-hint: ipv6 +checks: + exact-len: 16 + - +name: remote-ipv6-scope-id +type: u32 +doc: The scope id of the remote IPv6 address of the peer (RFC2553) + - +name: remote-port +type: u16 +doc: The remote port of the peer +byte-order: big-endian +checks: + min: 1 + - +name: socket +type: u32 +doc: The socket to be used to communicate with the peer + - +name: vpn-ipv4 +type: u32 +doc: The IPv4 address assigned to the peer by the server +byte-order: big-endian +display-hint: ipv4 + - +name: vpn-ipv6 +type: binary +doc: The IPv6 address assigned to the peer by the server +display-hint: ipv6 +checks: + exact-len: 16 + - +name: local-ipv4 +type: u32 +doc: The local IPv4 to be used to send packets to the peer (UDP only) +byte-order: big-endian +display-hint: ipv4 + - +name: local-ipv6 +type: binary +doc: The local IPv6 to be used to send packets to the peer (UDP only) +display-hint: ipv6 +checks: + exact-len: 16 + - +name: local-port +type: u16 +doc: The local port to be used to send packets to the peer (UDP only) +byte-order: big-endian +checks: + min: 1 + - +name: keepalive-interval +type: u32 +doc: | + The number of seconds after which a keep alive message is sent to the + peer + - +name: keepalive-timeout +type: u32 +doc: | + The number of seconds from the last activity after which the peer is + assumed dead + - +name: del-reason +type: u32 +doc: The reason why a peer was deleted +enum: del-peer-reason + - +name: vpn-rx-bytes +type: uint +doc: Number of bytes received over the tunnel + - +name: vpn-tx-bytes +type: uint +doc: Number of bytes transmitted over the tunnel + - +name: vpn-rx-packets +type: uint +doc: Number of packets received over the tunnel + - +name: vpn-tx-packets +type: uint +doc: Number of packets transmitted over the tunnel
[PATCH net-next v8 02/24] net: introduce OpenVPN Data Channel Offload (ovpn)
OpenVPN is a userspace software existing since around 2005 that allows users to create secure tunnels. So far OpenVPN has implemented all operations in userspace, which implies several back and forth between kernel and user land in order to process packets (encapsulate/decapsulate, encrypt/decrypt, rerouting..). With `ovpn` we intend to move the fast path (data channel) entirely in kernel space and thus improve user measured throughput over the tunnel. `ovpn` is implemented as a simple virtual network device driver, that can be manipulated by means of the standard RTNL APIs. A device of kind `ovpn` allows only IPv4/6 traffic and can be of type: * P2P (peer-to-peer): any packet sent over the interface will be encapsulated and transmitted to the other side (typical OpenVPN client or peer-to-peer behaviour); * P2MP (point-to-multipoint): packets sent over the interface are transmitted to peers based on existing routes (typical OpenVPN server behaviour). After the interface has been created, OpenVPN in userspace can configure it using a new Netlink API. Specifically it is possible to manage peers and their keys. The OpenVPN control channel is multiplexed over the same transport socket by means of OP codes. Anything that is not DATA_V2 (OpenVPN OP code for data traffic) is sent to userspace and handled there. This way the `ovpn` codebase is kept as compact as possible while focusing on handling data traffic only (fast path). Any OpenVPN control feature (like cipher negotiation, TLS handshake, rekeying, etc.) is still fully handled by the userspace process. When userspace establishes a new connection with a peer, it first performs the handshake and then passes the socket to the `ovpn` kernel module, which takes ownership. From this moment on `ovpn` will handle data traffic for the new peer. When control packets are received on the link, they are forwarded to userspace through the same transport socket they were received on, as userspace is still listening to them. Some events (like peer deletion) are sent to a Netlink multicast group. Although it wasn't easy to convince the community, `ovpn` implements only a limited number of the data-channel features supported by the userspace program. Each feature that made it to `ovpn` was attentively vetted to avoid carrying too much legacy along with us (and to give a clear cut to old and probalby-not-so-useful features). Notably, only encryption using AEAD ciphers (specifically ChaCha20Poly1305 and AES-GCM) was implemented. Supporting any other cipher out there was not deemed useful. Both UDP and TCP sockets ae supported. As explained above, in case of P2MP mode, OpenVPN will use the main system routing table to decide which packet goes to which peer. This implies that no routing table was re-implemented in the `ovpn` kernel module. This kernel module can be enabled by selecting the CONFIG_OVPN entry in the networking drivers section. NOTE: this first patch introduces the very basic framework only. Features are then added patch by patch, however, although each patch will compile and possibly not break at runtime, only after having applied the full set it is expected to see the ovpn module fully working. Cc: steffen.klass...@secunet.com Cc: antony.ant...@secunet.com Signed-off-by: Antonio Quartulli --- MAINTAINERS | 8 drivers/net/Kconfig | 14 ++ drivers/net/Makefile | 1 + drivers/net/ovpn/Makefile | 11 + drivers/net/ovpn/io.c | 22 ++ drivers/net/ovpn/io.h | 15 +++ drivers/net/ovpn/main.c | 109 ++ drivers/net/ovpn/main.h | 15 +++ include/uapi/linux/udp.h | 1 + 9 files changed, 196 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e71d066dc919404231446f8adfb291494e392883..0f02c9e1664fddb5ad45232e46b02c42aa27f1b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17448,6 +17448,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs.git F: Documentation/filesystems/overlayfs.rst F: fs/overlayfs/ +OPENVPN DATA CHANNEL OFFLOAD +M: Antonio Quartulli +L: openvpn-de...@lists.sourceforge.net (moderated for non-subscribers) +L: net...@vger.kernel.org +S: Maintained +T: git https://github.com/OpenVPN/linux-kernel-ovpn.git +F: drivers/net/ovpn/ + P54 WIRELESS DRIVER M: Christian Lamparter L: linux-wirel...@vger.kernel.org diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9920b3a68ed1582ab3af1cf9d4f6ae7bfd68ba7a..0055bcd2356c70bd9cfd8a944f549817dab8154f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -115,6 +115,20 @@ config WIREGUARD_DEBUG Say N here unless you know what you're doing. +config OVPN + tristate "OpenVPN data channel offload" + depends on NET && INET + select NET_UDP_TUNNEL + select DST_CACHE + select CRYPTO + select CRYPTO_A
[PATCH net-next v8 04/24] ovpn: add basic interface creation/destruction/management routines
Add basic infrastructure for handling ovpn interfaces. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 155 +- drivers/net/ovpn/main.h | 10 +++ drivers/net/ovpn/ovpnstruct.h | 8 +++ drivers/net/ovpn/packet.h | 40 +++ 4 files changed, 210 insertions(+), 3 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 0274bcd25080d7f85347f79aad0259771508..12f6258f94c8baef855e2ce90cf70380d5af5ca9 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -10,19 +10,58 @@ #include #include #include +#include #include +#include #include +#include #include #include "ovpnstruct.h" #include "main.h" #include "netlink.h" #include "io.h" +#include "packet.h" /* Driver info */ #define DRV_DESCRIPTION"OpenVPN data channel offload (ovpn)" #define DRV_COPYRIGHT "(C) 2020-2024 OpenVPN, Inc." +/** + * ovpn_struct_init - Initialize the netdevice private area + * @dev: the device to initialize + * @mode: device operation mode (i.e. p2p, mp, ..) + */ +static void ovpn_struct_init(struct net_device *dev, enum ovpn_mode mode) +{ + struct ovpn_struct *ovpn = netdev_priv(dev); + + ovpn->dev = dev; + ovpn->mode = mode; +} + +static void ovpn_struct_free(struct net_device *net) +{ +} + +static int ovpn_net_open(struct net_device *dev) +{ + netif_tx_start_all_queues(dev); + return 0; +} + +static int ovpn_net_stop(struct net_device *dev) +{ + netif_tx_stop_all_queues(dev); + return 0; +} + +static const struct net_device_ops ovpn_netdev_ops = { + .ndo_open = ovpn_net_open, + .ndo_stop = ovpn_net_stop, + .ndo_start_xmit = ovpn_net_xmit, +}; + /** * ovpn_dev_is_valid - check if the netdevice is of type 'ovpn' * @dev: the interface to check @@ -40,30 +79,140 @@ static struct rtnl_link_ops ovpn_link_ops = { .dellink = unregister_netdevice_queue, }; +static void ovpn_setup(struct net_device *dev) +{ + /* compute the overhead considering AEAD encryption */ + const int overhead = sizeof(u32) + NONCE_WIRE_SIZE + 16 + +sizeof(struct udphdr) + +max(sizeof(struct ipv6hdr), sizeof(struct iphdr)); + + netdev_features_t feat = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM | +NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | +NETIF_F_HIGHDMA; + + dev->needs_free_netdev = true; + + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + + dev->netdev_ops = &ovpn_netdev_ops; + dev->rtnl_link_ops = &ovpn_link_ops; + + dev->priv_destructor = ovpn_struct_free; + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->mtu = ETH_DATA_LEN - overhead; + dev->min_mtu = IPV4_MIN_MTU; + dev->max_mtu = IP_MAX_MTU - overhead; + + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + + dev->lltx = true; + dev->features |= feat; + dev->hw_features |= feat; + dev->hw_enc_features |= feat; + + dev->needed_headroom = OVPN_HEAD_ROOM; + dev->needed_tailroom = OVPN_MAX_PADDING; +} + +/** + * ovpn_iface_create - create and initialize a new 'ovpn' netdevice + * @name: the name of the new device + * @mode: the OpenVPN mode to set this device to + * @net: the netns this device should be created in + * + * A new netdevice is created and registered. + * Its private area is initialized with an empty ovpn_struct object. + * + * Return: a pointer to the new device on success or a negative error code + * otherwise + */ +struct net_device *ovpn_iface_create(const char *name, enum ovpn_mode mode, +struct net *net) +{ + struct net_device *dev; + int ret; + + dev = alloc_netdev(sizeof(struct ovpn_struct), name, NET_NAME_USER, + ovpn_setup); + if (!dev) + return ERR_PTR(-ENOMEM); + + dev_net_set(dev, net); + ovpn_struct_init(dev, mode); + + rtnl_lock(); + ret = register_netdevice(dev); + if (ret < 0) { + netdev_err(dev, "cannot register interface: %d\n", ret); + rtnl_unlock(); + goto err; + } + /* turn carrier explicitly off after registration, this way state is +* clearly defined +*/ + netif_carrier_off(dev); + rtnl_unlock(); + + return dev; + +err: + free_netdev(dev); + return ERR_PTR(ret); +} + +/** + * ovpn_iface_destruct - tear down netdevice + * @ovpn: the ovpn instance objected related to the interface to tear down + * + * This function takes care of te
[PATCH net-next v8 00/24] Introducing OpenVPN Data Channel Offload
This is the 8th version of the ovpn patchset. Thanks Sergey for arguing regarding splitting PEER_SET into SET and NEW. I decided to follow this suggestion as it makes the API and its return value easier to work with. Thanks Donald for the suggestions regarding the NL API - they have all been implemented (unless I forgot some, but hopefully I did not). Notable changes from v7: * Netlink API adjustments: ** renamed NL API from OP_OBJ to OBJ_OP (i.e. from SET_PEER to PEER_SET) ** split PEER_SET from PEER_NEW for better clarity in case of error ** renamed NL API from NEW/DEL_IFACE to DEV_NEW/DEL ** converted all underscores to dashes in YML NL spec ** split sockaddr_remote attr into ipv4/6, port and v6_scope_id attrs ** split local_ip attr into local_ipv4 and local_ipv6 attrs ** turned keyconf into a root attribute (it was nested in peer before) ** made key_swap use a keyconf object rather than a peer for consistency with key mgmt API ** created specific op for peer_del notification (peer_del_ntf) ** created specific op for key_swap notification (key_swap_ntf) ** allow user to update VPN IPv4/6 (peer is now rehashable) ** converted port attrs from u32 to u16 for better consistency with userspace code * added rtnl_ops .dellink implementation * removed patch 2 as it's not needed anymore thanks to the point above * moved rtnl_ops .kind initialization to first patch * updated MAINTAINERS file with Github tree and selftest folder * wrapped long lines in selftest scripts BONUS: used b4 for the first time to prepare the patchset and send it Please note that patches previously reviewed by Andrew Lunn have retained the Reviewed-by tag as they have been simply rebased without any modification. The latest code can also be found at: https://github.com/OpenVPN/linux-kernel-ovpn Thanks a lot! Best Regards, Antonio Quartulli OpenVPN Inc. --- Antonio Quartulli (24): netlink: add NLA_POLICY_MAX_LEN macro net: introduce OpenVPN Data Channel Offload (ovpn) ovpn: add basic netlink support ovpn: add basic interface creation/destruction/management routines ovpn: implement interface creation/destruction via netlink ovpn: keep carrier always on ovpn: introduce the ovpn_peer object ovpn: introduce the ovpn_socket object ovpn: implement basic TX path (UDP) ovpn: implement basic RX path (UDP) ovpn: implement packet processing ovpn: store tunnel and transport statistics ovpn: implement TCP transport ovpn: implement multi-peer support ovpn: implement peer lookup logic ovpn: implement keepalive mechanism ovpn: add support for updating local UDP endpoint ovpn: add support for peer floating ovpn: implement peer add/dump/delete via netlink ovpn: implement key add/del/swap via netlink ovpn: kill key and notify userspace in case of IV exhaustion ovpn: notify userspace when a peer is deleted ovpn: add basic ethtool support testing/selftest: add test tool and scripts for ovpn module Documentation/netlink/specs/ovpn.yaml | 387 + MAINTAINERS | 11 + drivers/net/Kconfig | 15 + drivers/net/Makefile |1 + drivers/net/ovpn/Makefile | 22 + drivers/net/ovpn/bind.c | 54 + drivers/net/ovpn/bind.h | 117 ++ drivers/net/ovpn/crypto.c | 172 ++ drivers/net/ovpn/crypto.h | 138 ++ drivers/net/ovpn/crypto_aead.c| 356 drivers/net/ovpn/crypto_aead.h| 31 + drivers/net/ovpn/io.c | 459 ++ drivers/net/ovpn/io.h | 25 + drivers/net/ovpn/main.c | 363 drivers/net/ovpn/main.h | 29 + drivers/net/ovpn/netlink-gen.c| 224 +++ drivers/net/ovpn/netlink-gen.h| 42 + drivers/net/ovpn/netlink.c| 1099 + drivers/net/ovpn/netlink.h| 18 + drivers/net/ovpn/ovpnstruct.h | 60 + drivers/net/ovpn/packet.h | 40 + drivers/net/ovpn/peer.c | 1207 ++ drivers/net/ovpn/peer.h | 172 ++ drivers/net/ovpn/pktid.c | 130 ++ drivers/net/ovpn/pktid.h | 87 + drivers/net/ovpn/proto.h | 104 ++ drivers/net/ovpn/skb.h| 61 + drivers/net/ovpn/socket.c | 165 ++ drivers/net/ovpn/socket.h | 53 + drivers/net/ovpn/stats.c | 21 + drivers/net/ovpn/stats.h | 47 + drivers/net/ovpn/
[PATCH net-next v8 01/24] netlink: add NLA_POLICY_MAX_LEN macro
Similarly to NLA_POLICY_MIN_LEN, NLA_POLICY_MAX_LEN defines a policy with a maximum length value. The netlink generator for YAML specs has been extended accordingly. Cc: donald.hun...@gmail.com Signed-off-by: Antonio Quartulli --- include/net/netlink.h | 1 + tools/net/ynl/ynl-gen-c.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/net/netlink.h b/include/net/netlink.h index db6af207287c839408c58cb28b82408e0548eaca..2dc671c977ff3297975269d236264907009703d3 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -469,6 +469,7 @@ struct nla_policy { .max = _len \ } #define NLA_POLICY_MIN_LEN(_len) NLA_POLICY_MIN(NLA_BINARY, _len) +#define NLA_POLICY_MAX_LEN(_len) NLA_POLICY_MAX(NLA_BINARY, _len) /** * struct nl_info - netlink source information diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py index 717530bc9c52e7cfa897814870b4583c88618a27..3ccbb301be87f80bbcf03da63d60f58c4fedc1c8 100755 --- a/tools/net/ynl/ynl-gen-c.py +++ b/tools/net/ynl/ynl-gen-c.py @@ -466,6 +466,8 @@ class TypeBinary(Type): def _attr_policy(self, policy): if 'exact-len' in self.checks: mem = 'NLA_POLICY_EXACT_LEN(' + str(self.get_limit('exact-len')) + ')' +elif 'max-len' in self.checks: +mem = 'NLA_POLICY_MAX_LEN(' + str(self.get_limit('max-len')) + ')' else: mem = '{ ' if len(self.checks) == 1 and 'min-len' in self.checks: -- 2.45.2
[PATCH net-next v8 05/24] ovpn: implement interface creation/destruction via netlink
Allow userspace to create and destroy an interface using netlink commands. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.h| 2 ++ drivers/net/ovpn/netlink.c | 59 -- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/main.h b/drivers/net/ovpn/main.h index 4dfcba9deb590bbf119f51a40dff1517fe227b22..c664d9c655734263fcf58dd8f2fa5446565a29cf 100644 --- a/drivers/net/ovpn/main.h +++ b/drivers/net/ovpn/main.h @@ -10,6 +10,8 @@ #ifndef _NET_OVPN_MAIN_H_ #define _NET_OVPN_MAIN_H_ +#define OVPN_DEFAULT_IFNAME "ovpn%d" + struct net_device *ovpn_iface_create(const char *name, enum ovpn_mode mode, struct net *net); void ovpn_iface_destruct(struct ovpn_struct *ovpn); diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 7b6b4d03b845eeb8654e37ac3495e8172ac3f291..6e60591d605dde19c6bbd47ef0e90e522776688c 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -84,12 +85,66 @@ void ovpn_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb, int ovpn_nl_dev_new_doit(struct sk_buff *skb, struct genl_info *info) { - return -EOPNOTSUPP; + const char *ifname = OVPN_DEFAULT_IFNAME; + enum ovpn_mode mode = OVPN_MODE_P2P; + struct net_device *dev; + struct sk_buff *msg; + void *hdr; + + if (info->attrs[OVPN_A_IFNAME]) + ifname = nla_data(info->attrs[OVPN_A_IFNAME]); + + if (info->attrs[OVPN_A_MODE]) { + mode = nla_get_u32(info->attrs[OVPN_A_MODE]); + pr_debug("ovpn: setting device (%s) mode: %u\n", ifname, mode); + } + + dev = ovpn_iface_create(ifname, mode, genl_info_net(info)); + if (IS_ERR(dev)) { + NL_SET_ERR_MSG_FMT_MOD(info->extack, + "error while creating interface: %ld", + PTR_ERR(dev)); + return PTR_ERR(dev); + } + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_iput(msg, info); + if (!hdr) { + nlmsg_free(msg); + return -ENOBUFS; + } + + if (nla_put_string(msg, OVPN_A_IFNAME, dev->name)) { + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + return -EMSGSIZE; + } + + if (nla_put_u32(msg, OVPN_A_IFINDEX, dev->ifindex)) { + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + return -EMSGSIZE; + } + + genlmsg_end(msg, hdr); + + return genlmsg_reply(msg, info); } int ovpn_nl_dev_del_doit(struct sk_buff *skb, struct genl_info *info) { - return -EOPNOTSUPP; + struct ovpn_struct *ovpn = info->user_ptr[0]; + + rtnl_lock(); + ovpn_iface_destruct(ovpn); + unregister_netdevice(ovpn->dev); + netdev_put(ovpn->dev, NULL); + rtnl_unlock(); + + return 0; } int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info) -- 2.45.2
[PATCH net-next v8 06/24] ovpn: keep carrier always on
An ovpn interface will keep carrier always on and let the user decide when an interface should be considered disconnected. This way, even if an ovpn interface is not connected to any peer, it can still retain all IPs and routes and thus prevent any data leak. Signed-off-by: Antonio Quartulli Reviewed-by: Andrew Lunn --- drivers/net/ovpn/main.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 12f6258f94c8baef855e2ce90cf70380d5af5ca9..87d49b83107aa9c276c5b200ac919965379c99be 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -46,6 +46,13 @@ static void ovpn_struct_free(struct net_device *net) static int ovpn_net_open(struct net_device *dev) { + /* ovpn keeps the carrier always on to avoid losing IP or route +* configuration upon disconnection. This way it can prevent leaks +* of traffic outside of the VPN tunnel. +* The user may override this behaviour by tearing down the interface +* manually. +*/ + netif_carrier_on(dev); netif_tx_start_all_queues(dev); return 0; } -- 2.45.2
[PATCH net-next v8 08/24] ovpn: introduce the ovpn_socket object
This specific structure is used in the ovpn kernel module to wrap and carry around a standard kernel socket. ovpn takes ownership of passed sockets and therefore an ovpn specific objects is attached to them for status tracking purposes. Initially only UDP support is introduced. TCP will come in a later patch. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 2 + drivers/net/ovpn/socket.c | 120 ++ drivers/net/ovpn/socket.h | 48 +++ drivers/net/ovpn/udp.c| 72 drivers/net/ovpn/udp.h| 17 +++ 5 files changed, 259 insertions(+) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index ce13499b3e1775a7f2a9ce16c6cb0aa088f93685..56bddc9bef83e0befde6af3c3565bb91731d7b22 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -13,3 +13,5 @@ ovpn-y += io.o ovpn-y += netlink.o ovpn-y += netlink-gen.o ovpn-y += peer.o +ovpn-y += socket.o +ovpn-y += udp.o diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c new file mode 100644 index ..090a3232ab0ec19702110f1a90f45c7f10889f6f --- /dev/null +++ b/drivers/net/ovpn/socket.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OpenVPN data channel offload + * + * Copyright (C) 2020-2024 OpenVPN, Inc. + * + * Author:James Yonan + * Antonio Quartulli + */ + +#include +#include + +#include "ovpnstruct.h" +#include "main.h" +#include "io.h" +#include "peer.h" +#include "socket.h" +#include "udp.h" + +static void ovpn_socket_detach(struct socket *sock) +{ + if (!sock) + return; + + sockfd_put(sock); +} + +/** + * ovpn_socket_release_kref - kref_put callback + * @kref: the kref object + */ +void ovpn_socket_release_kref(struct kref *kref) +{ + struct ovpn_socket *sock = container_of(kref, struct ovpn_socket, + refcount); + + ovpn_socket_detach(sock->sock); + kfree_rcu(sock, rcu); +} + +static bool ovpn_socket_hold(struct ovpn_socket *sock) +{ + return kref_get_unless_zero(&sock->refcount); +} + +static struct ovpn_socket *ovpn_socket_get(struct socket *sock) +{ + struct ovpn_socket *ovpn_sock; + + rcu_read_lock(); + ovpn_sock = rcu_dereference_sk_user_data(sock->sk); + if (!ovpn_socket_hold(ovpn_sock)) { + pr_warn("%s: found ovpn_socket with ref = 0\n", __func__); + ovpn_sock = NULL; + } + rcu_read_unlock(); + + return ovpn_sock; +} + +static int ovpn_socket_attach(struct socket *sock, struct ovpn_peer *peer) +{ + int ret = -EOPNOTSUPP; + + if (!sock || !peer) + return -EINVAL; + + if (sock->sk->sk_protocol == IPPROTO_UDP) + ret = ovpn_udp_socket_attach(sock, peer->ovpn); + + return ret; +} + +/** + * ovpn_socket_new - create a new socket and initialize it + * @sock: the kernel socket to embed + * @peer: the peer reachable via this socket + * + * Return: an openvpn socket on success or a negative error code otherwise + */ +struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) +{ + struct ovpn_socket *ovpn_sock; + int ret; + + ret = ovpn_socket_attach(sock, peer); + if (ret < 0 && ret != -EALREADY) + return ERR_PTR(ret); + + /* if this socket is already owned by this interface, just increase the +* refcounter and use it as expected. +* +* Since UDP sockets can be used to talk to multiple remote endpoints, +* openvpn normally instantiates only one socket and shares it among all +* its peers. For this reason, when we find out that a socket is already +* used for some other peer in *this* instance, we can happily increase +* its refcounter and use it normally. +*/ + if (ret == -EALREADY) { + /* caller is expected to increase the sock refcounter before +* passing it to this function. For this reason we drop it if +* not needed, like when this socket is already owned. +*/ + ovpn_sock = ovpn_socket_get(sock); + sockfd_put(sock); + return ovpn_sock; + } + + ovpn_sock = kzalloc(sizeof(*ovpn_sock), GFP_KERNEL); + if (!ovpn_sock) + return ERR_PTR(-ENOMEM); + + ovpn_sock->ovpn = peer->ovpn; + ovpn_sock->sock = sock; + kref_init(&ovpn_sock->refcount); + + rcu_assign_sk_user_data(sock->sk, ovpn_sock); + + return ovpn_sock; +} diff --git a/drivers/net/ovpn/socket.h b/drivers/net/ovpn/socket.h new file mode 100644 index ..5ad9c5073b085482da95ee8ebf40acf20bf2e4b3 ---
[PATCH net-next v8 09/24] ovpn: implement basic TX path (UDP)
Packets sent over the ovpn interface are processed and transmitted to the connected peer, if any. Implementation is UDP only. TCP will be added by a later patch. Note: no crypto/encapsulation exists yet. packets are just captured and sent. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 137 +++- drivers/net/ovpn/peer.c | 37 +++- drivers/net/ovpn/peer.h | 5 ++ drivers/net/ovpn/skb.h | 51 +++ drivers/net/ovpn/udp.c | 232 drivers/net/ovpn/udp.h | 8 ++ 6 files changed, 467 insertions(+), 3 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index ad3813419c33cbdfe7e8ad6f5c8b444a3540a69f..dfd2c90c56842374b7fb1c8bead061a76272441e 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -9,14 +9,149 @@ #include #include +#include #include "io.h" +#include "ovpnstruct.h" +#include "peer.h" +#include "udp.h" +#include "skb.h" + +static void ovpn_encrypt_post(struct sk_buff *skb, int ret) +{ + struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; + + if (unlikely(ret < 0)) + goto err; + + skb_mark_not_on_list(skb); + + switch (peer->sock->sock->sk->sk_protocol) { + case IPPROTO_UDP: + ovpn_udp_send_skb(peer->ovpn, peer, skb); + break; + default: + /* no transport configured yet */ + goto err; + } + /* skb passed down the stack - don't free it */ + skb = NULL; +err: + if (unlikely(skb)) + dev_core_stats_tx_dropped_inc(peer->ovpn->dev); + ovpn_peer_put(peer); + kfree_skb(skb); +} + +static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb) +{ + ovpn_skb_cb(skb)->peer = peer; + + /* take a reference to the peer because the crypto code may run async. +* ovpn_encrypt_post() will release it upon completion +*/ + if (unlikely(!ovpn_peer_hold(peer))) { + DEBUG_NET_WARN_ON_ONCE(1); + return false; + } + + ovpn_encrypt_post(skb, 0); + return true; +} + +/* send skb to connected peer, if any */ +static void ovpn_send(struct ovpn_struct *ovpn, struct sk_buff *skb, + struct ovpn_peer *peer) +{ + struct sk_buff *curr, *next; + + if (likely(!peer)) + /* retrieve peer serving the destination IP of this packet */ + peer = ovpn_peer_get_by_dst(ovpn, skb); + if (unlikely(!peer)) { + net_dbg_ratelimited("%s: no peer to send data to\n", + ovpn->dev->name); + dev_core_stats_tx_dropped_inc(ovpn->dev); + goto drop; + } + + /* this might be a GSO-segmented skb list: process each skb +* independently +*/ + skb_list_walk_safe(skb, curr, next) + if (unlikely(!ovpn_encrypt_one(peer, curr))) { + dev_core_stats_tx_dropped_inc(ovpn->dev); + kfree_skb(curr); + } + + /* skb passed over, no need to free */ + skb = NULL; +drop: + if (likely(peer)) + ovpn_peer_put(peer); + kfree_skb_list(skb); +} /* Send user data to the network */ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) { + struct ovpn_struct *ovpn = netdev_priv(dev); + struct sk_buff *segments, *curr, *next; + struct sk_buff_head skb_list; + __be16 proto; + int ret; + + /* reset netfilter state */ + nf_reset_ct(skb); + + /* verify IP header size in network packet */ + proto = ovpn_ip_check_protocol(skb); + if (unlikely(!proto || skb->protocol != proto)) { + net_err_ratelimited("%s: dropping malformed payload packet\n", + dev->name); + dev_core_stats_tx_dropped_inc(ovpn->dev); + goto drop; + } + + if (skb_is_gso(skb)) { + segments = skb_gso_segment(skb, 0); + if (IS_ERR(segments)) { + ret = PTR_ERR(segments); + net_err_ratelimited("%s: cannot segment packet: %d\n", + dev->name, ret); + dev_core_stats_tx_dropped_inc(ovpn->dev); + goto drop; + } + + consume_skb(skb); + skb = segments; + } + + /* from this moment on, "skb" might be a list */ + + __skb_queue_head_init(&skb_list); + skb_list_walk_safe(skb, curr, next) { + skb_mark_not_on_list(curr); + + curr = skb_share_check(curr, GFP_ATOMIC); +
[PATCH net-next v8 10/24] ovpn: implement basic RX path (UDP)
Packets received over the socket are forwarded to the user device. Implementation is UDP only. TCP will be added by a later patch. Note: no decryption/decapsulation exists yet, packets are forwarded as they arrive without much processing. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 66 ++- drivers/net/ovpn/io.h | 2 + drivers/net/ovpn/main.c | 8 +++- drivers/net/ovpn/ovpnstruct.h | 3 ++ drivers/net/ovpn/proto.h | 75 ++ drivers/net/ovpn/socket.c | 24 ++ drivers/net/ovpn/udp.c| 104 +- drivers/net/ovpn/udp.h| 3 +- 8 files changed, 281 insertions(+), 4 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index dfd2c90c56842374b7fb1c8bead061a76272441e..78449b52a2f8f710107d2493299c6aca9a2cb2ce 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -9,14 +9,78 @@ #include #include +#include #include -#include "io.h" #include "ovpnstruct.h" #include "peer.h" +#include "io.h" +#include "netlink.h" +#include "proto.h" #include "udp.h" #include "skb.h" +/* Called after decrypt to write the IP packet to the device. + * This method is expected to manage/free the skb. + */ +static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb) +{ + unsigned int pkt_len; + + /* we can't guarantee the packet wasn't corrupted before entering the +* VPN, therefore we give other layers a chance to check that +*/ + skb->ip_summed = CHECKSUM_NONE; + + /* skb hash for transport packet no longer valid after decapsulation */ + skb_clear_hash(skb); + + /* post-decrypt scrub -- prepare to inject encapsulated packet onto the +* interface, based on __skb_tunnel_rx() in dst.h +*/ + skb->dev = peer->ovpn->dev; + skb_set_queue_mapping(skb, 0); + skb_scrub_packet(skb, true); + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_probe_transport_header(skb); + skb_reset_inner_headers(skb); + + memset(skb->cb, 0, sizeof(skb->cb)); + + /* cause packet to be "received" by the interface */ + pkt_len = skb->len; + if (likely(gro_cells_receive(&peer->ovpn->gro_cells, +skb) == NET_RX_SUCCESS)) + /* update RX stats with the size of decrypted packet */ + dev_sw_netstats_rx_add(peer->ovpn->dev, pkt_len); +} + +static void ovpn_decrypt_post(struct sk_buff *skb, int ret) +{ + struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; + + if (unlikely(ret < 0)) + goto drop; + + ovpn_netdev_write(peer, skb); + /* skb is passed to upper layer - don't free it */ + skb = NULL; +drop: + if (unlikely(skb)) + dev_core_stats_rx_dropped_inc(peer->ovpn->dev); + ovpn_peer_put(peer); + kfree_skb(skb); +} + +/* pick next packet from RX queue, decrypt and forward it to the device */ +void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb) +{ + ovpn_skb_cb(skb)->peer = peer; + ovpn_decrypt_post(skb, 0); +} + static void ovpn_encrypt_post(struct sk_buff *skb, int ret) { struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; diff --git a/drivers/net/ovpn/io.h b/drivers/net/ovpn/io.h index aa259be66441f7b0262f39da12d6c3dce0a9b24c..9667a0a470e0b4b427524fffb5b9b395007e5a2f 100644 --- a/drivers/net/ovpn/io.h +++ b/drivers/net/ovpn/io.h @@ -12,4 +12,6 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev); +void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb); + #endif /* _NET_OVPN_OVPN_H_ */ diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 4522f2f8978614c96d529f807bd2cf32a44f8216..670e66f8f750086de5d48e4708721d102c149a2e 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -13,6 +13,7 @@ #include //#include #include +#include #include #include #include @@ -45,11 +46,16 @@ static void ovpn_struct_init(struct net_device *dev, enum ovpn_mode mode) static void ovpn_struct_free(struct net_device *net) { + struct ovpn_struct *ovpn = netdev_priv(net); + + gro_cells_destroy(&ovpn->gro_cells); } static int ovpn_net_init(struct net_device *dev) { - return 0; + struct ovpn_struct *ovpn = netdev_priv(dev); + + return gro_cells_init(&ovpn->gro_cells, dev); } static int ovpn_net_open(struct net_device *dev) diff --git a/drivers/net/ovpn/ovpnstruct.h b/drivers/net/ovpn/ovpnstruct.h index 25f4837b798b1a2c9b11763dfb5b44e8ec337e6f..65497ce115aa4be719ed03f89135a80b73f9ef9b 100644 --- a/drivers/net/ovpn/ovpnstruct.h +++ b/drivers/net/ovpn/ovpnstru
[PATCH net-next v8 07/24] ovpn: introduce the ovpn_peer object
An ovpn_peer object holds the whole status of a remote peer (regardless whether it is a server or a client). This includes status for crypto, tx/rx buffers, napi, etc. Only support for one peer is introduced (P2P mode). Multi peer support is introduced with a later patch. Along with the ovpn_peer, also the ovpn_bind object is introcued as the two are strictly related. An ovpn_bind object wraps a sockaddr representing the local coordinates being used to talk to a specific peer. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 2 + drivers/net/ovpn/bind.c | 58 +++ drivers/net/ovpn/bind.h | 117 ++ drivers/net/ovpn/main.c | 12 ++ drivers/net/ovpn/main.h | 2 + drivers/net/ovpn/ovpnstruct.h | 7 + drivers/net/ovpn/peer.c | 355 ++ drivers/net/ovpn/peer.h | 82 ++ 8 files changed, 635 insertions(+) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index 201dc001419f1d99ae95c0ee0f96e68f8a4eac16..ce13499b3e1775a7f2a9ce16c6cb0aa088f93685 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -7,7 +7,9 @@ # Author: Antonio Quartulli obj-$(CONFIG_OVPN) := ovpn.o +ovpn-y += bind.o ovpn-y += main.o ovpn-y += io.o ovpn-y += netlink.o ovpn-y += netlink-gen.o +ovpn-y += peer.o diff --git a/drivers/net/ovpn/bind.c b/drivers/net/ovpn/bind.c new file mode 100644 index ..b4d2ccec2ceddf43bc445b489cc62a578ef0ad0a --- /dev/null +++ b/drivers/net/ovpn/bind.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OpenVPN data channel offload + * + * Copyright (C) 2012-2024 OpenVPN, Inc. + * + * Author:James Yonan + * Antonio Quartulli + */ + +#include +#include + +#include "ovpnstruct.h" +#include "bind.h" +#include "peer.h" + +/** + * ovpn_bind_from_sockaddr - retrieve binding matching sockaddr + * @ss: the sockaddr to match + * + * Return: the bind matching the passed sockaddr if found, NULL otherwise + */ +struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss) +{ + struct ovpn_bind *bind; + size_t sa_len; + + if (ss->ss_family == AF_INET) + sa_len = sizeof(struct sockaddr_in); + else if (ss->ss_family == AF_INET6) + sa_len = sizeof(struct sockaddr_in6); + else + return ERR_PTR(-EAFNOSUPPORT); + + bind = kzalloc(sizeof(*bind), GFP_ATOMIC); + if (unlikely(!bind)) + return ERR_PTR(-ENOMEM); + + memcpy(&bind->remote, ss, sa_len); + + return bind; +} + +/** + * ovpn_bind_reset - assign new binding to peer + * @peer: the peer whose binding has to be replaced + * @new: the new bind to assign + */ +void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *new) +{ + struct ovpn_bind *old; + + spin_lock_bh(&peer->lock); + old = rcu_replace_pointer(peer->bind, new, true); + spin_unlock_bh(&peer->lock); + + kfree_rcu(old, rcu); +} diff --git a/drivers/net/ovpn/bind.h b/drivers/net/ovpn/bind.h new file mode 100644 index ..859213d5040deb36c416eafcf5c6ab31c4d52c7a --- /dev/null +++ b/drivers/net/ovpn/bind.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* OpenVPN data channel offload + * + * Copyright (C) 2012-2024 OpenVPN, Inc. + * + * Author:James Yonan + * Antonio Quartulli + */ + +#ifndef _NET_OVPN_OVPNBIND_H_ +#define _NET_OVPN_OVPNBIND_H_ + +#include +#include +#include +#include +#include +#include + +struct ovpn_peer; + +/** + * union ovpn_sockaddr - basic transport layer address + * @in4: IPv4 address + * @in6: IPv6 address + */ +union ovpn_sockaddr { + struct sockaddr_in in4; + struct sockaddr_in6 in6; +}; + +/** + * struct ovpn_bind - remote peer binding + * @remote: the remote peer sockaddress + * @local: local endpoint used to talk to the peer + * @local.ipv4: local IPv4 used to talk to the peer + * @local.ipv6: local IPv6 used to talk to the peer + * @rcu: used to schedule RCU cleanup job + */ +struct ovpn_bind { + union ovpn_sockaddr remote; /* remote sockaddr */ + + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } local; + + struct rcu_head rcu; +}; + +/** + * skb_protocol_to_family - translate skb->protocol to AF_INET or AF_INET6 + * @skb: the packet sk_buff to inspect + * + * Return: AF_INET, AF_INET6 or 0 in case of unknown protocol + */ +static inline unsigned short skb_protocol_to_family(const struct sk_buff *skb) +{ + switch (skb->protocol) { + case htons(ETH_P_IP): + return AF_INET; + case htons(ETH_P_IPV6): + return AF_INET6; + default: + return 0; + } +} + +/** + * ovpn_bind_skb_src_match - match packet
[PATCH net-next v8 11/24] ovpn: implement packet processing
This change implements encryption/decryption and encapsulation/decapsulation of OpenVPN packets. Support for generic crypto state is added along with a wrapper for the AEAD crypto kernel API. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 3 + drivers/net/ovpn/crypto.c | 153 ++ drivers/net/ovpn/crypto.h | 136 drivers/net/ovpn/crypto_aead.c | 354 + drivers/net/ovpn/crypto_aead.h | 31 drivers/net/ovpn/io.c | 140 ++-- drivers/net/ovpn/io.h | 3 + drivers/net/ovpn/packet.h | 2 +- drivers/net/ovpn/peer.c| 29 drivers/net/ovpn/peer.h| 6 + drivers/net/ovpn/pktid.c | 130 +++ drivers/net/ovpn/pktid.h | 87 ++ drivers/net/ovpn/proto.h | 31 drivers/net/ovpn/skb.h | 11 +- 14 files changed, 1104 insertions(+), 12 deletions(-) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index 56bddc9bef83e0befde6af3c3565bb91731d7b22..ccdaeced1982c851475657860a005ff2b9dfbd13 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -8,10 +8,13 @@ obj-$(CONFIG_OVPN) := ovpn.o ovpn-y += bind.o +ovpn-y += crypto.o +ovpn-y += crypto_aead.o ovpn-y += main.o ovpn-y += io.o ovpn-y += netlink.o ovpn-y += netlink-gen.o ovpn-y += peer.o +ovpn-y += pktid.o ovpn-y += socket.o ovpn-y += udp.o diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c new file mode 100644 index ..f1f7510e2f735e367f96eb4982ba82c9af3c8bfc --- /dev/null +++ b/drivers/net/ovpn/crypto.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OpenVPN data channel offload + * + * Copyright (C) 2020-2024 OpenVPN, Inc. + * + * Author:James Yonan + * Antonio Quartulli + */ + +#include +#include +#include +#include + +#include "ovpnstruct.h" +#include "main.h" +#include "packet.h" +#include "pktid.h" +#include "crypto_aead.h" +#include "crypto.h" + +static void ovpn_ks_destroy_rcu(struct rcu_head *head) +{ + struct ovpn_crypto_key_slot *ks; + + ks = container_of(head, struct ovpn_crypto_key_slot, rcu); + ovpn_aead_crypto_key_slot_destroy(ks); +} + +void ovpn_crypto_key_slot_release(struct kref *kref) +{ + struct ovpn_crypto_key_slot *ks; + + ks = container_of(kref, struct ovpn_crypto_key_slot, refcount); + call_rcu(&ks->rcu, ovpn_ks_destroy_rcu); +} + +/* can only be invoked when all peer references have been dropped (i.e. RCU + * release routine) + */ +void ovpn_crypto_state_release(struct ovpn_crypto_state *cs) +{ + struct ovpn_crypto_key_slot *ks; + + ks = rcu_access_pointer(cs->slots[0]); + if (ks) { + RCU_INIT_POINTER(cs->slots[0], NULL); + ovpn_crypto_key_slot_put(ks); + } + + ks = rcu_access_pointer(cs->slots[1]); + if (ks) { + RCU_INIT_POINTER(cs->slots[1], NULL); + ovpn_crypto_key_slot_put(ks); + } +} + +/* Reset the ovpn_crypto_state object in a way that is atomic + * to RCU readers. + */ +int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs, + const struct ovpn_peer_key_reset *pkr) +{ + struct ovpn_crypto_key_slot *old = NULL, *new; + u8 idx; + + if (pkr->slot != OVPN_KEY_SLOT_PRIMARY && + pkr->slot != OVPN_KEY_SLOT_SECONDARY) + return -EINVAL; + + new = ovpn_aead_crypto_key_slot_new(&pkr->key); + if (IS_ERR(new)) + return PTR_ERR(new); + + spin_lock_bh(&cs->lock); + idx = cs->primary_idx; + switch (pkr->slot) { + case OVPN_KEY_SLOT_PRIMARY: + old = rcu_replace_pointer(cs->slots[idx], new, + lockdep_is_held(&cs->lock)); + break; + case OVPN_KEY_SLOT_SECONDARY: + old = rcu_replace_pointer(cs->slots[!idx], new, + lockdep_is_held(&cs->lock)); + break; + } + spin_unlock_bh(&cs->lock); + + if (old) + ovpn_crypto_key_slot_put(old); + + return 0; +} + +void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs, +enum ovpn_key_slot slot) +{ + struct ovpn_crypto_key_slot *ks = NULL; + u8 idx; + + if (slot != OVPN_KEY_SLOT_PRIMARY && + slot != OVPN_KEY_SLOT_SECONDARY) { + pr_warn("Invalid slot to release: %u\n", slot); + return; + } + + spin_lock_bh(&cs->lock); + idx = cs->primary_idx; + switch (slot) { + case OVPN_KEY_SLOT_PRIMARY: + ks = rcu_replace_pointer(cs->slots[idx], NULL,
[PATCH net-next v8 12/24] ovpn: store tunnel and transport statistics
Byte/packet counters for in-tunnel and transport streams are now initialized and updated as needed. To be exported via netlink. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 1 + drivers/net/ovpn/crypto_aead.c | 2 ++ drivers/net/ovpn/io.c | 12 +++ drivers/net/ovpn/peer.c| 3 +++ drivers/net/ovpn/peer.h| 8 +++ drivers/net/ovpn/skb.h | 1 + drivers/net/ovpn/stats.c | 21 +++ drivers/net/ovpn/stats.h | 47 ++ 8 files changed, 95 insertions(+) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index ccdaeced1982c851475657860a005ff2b9dfbd13..d43fda72646bdc7644d9a878b56da0a0e5680c98 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -17,4 +17,5 @@ ovpn-y += netlink-gen.o ovpn-y += peer.o ovpn-y += pktid.o ovpn-y += socket.o +ovpn-y += stats.o ovpn-y += udp.o diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c index 97134ac679c67a3d44a0bf49c2ddf058fd7c0e99..6599c8550390a60d3b0fb9c144beb8c7871bc320 100644 --- a/drivers/net/ovpn/crypto_aead.c +++ b/drivers/net/ovpn/crypto_aead.c @@ -128,6 +128,7 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks, aead_request_set_crypt(req, sg, sg, skb->len - head_size, iv); aead_request_set_ad(req, OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE); + ovpn_skb_cb(skb)->ctx->orig_len = skb->len; ovpn_skb_cb(skb)->ctx->peer = peer; ovpn_skb_cb(skb)->ctx->req = req; ovpn_skb_cb(skb)->ctx->ks = ks; @@ -216,6 +217,7 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks, aead_request_set_ad(req, NONCE_WIRE_SIZE + OVPN_OP_SIZE_V2); + ovpn_skb_cb(skb)->ctx->orig_len = skb->len; ovpn_skb_cb(skb)->ctx->payload_offset = payload_offset; ovpn_skb_cb(skb)->ctx->peer = peer; ovpn_skb_cb(skb)->ctx->req = req; diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index a75aa301b99e6abff04d5cb289b26b5979906ae5..d54af1a0f03a33b03001a041e62554b35a7f5ebc 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "ovpnstruct.h" #include "peer.h" @@ -20,6 +21,7 @@ #include "crypto_aead.h" #include "netlink.h" #include "proto.h" +#include "socket.h" #include "udp.h" #include "skb.h" @@ -66,6 +68,7 @@ void ovpn_decrypt_post(void *data, int ret) unsigned int payload_offset = 0; struct ovpn_peer *peer = NULL; struct sk_buff *skb = data; + unsigned int orig_len = 0; __be16 proto; __be32 *pid; @@ -80,6 +83,7 @@ void ovpn_decrypt_post(void *data, int ret) payload_offset = ovpn_skb_cb(skb)->ctx->payload_offset; ks = ovpn_skb_cb(skb)->ctx->ks; peer = ovpn_skb_cb(skb)->ctx->peer; + orig_len = ovpn_skb_cb(skb)->ctx->orig_len; aead_request_free(ovpn_skb_cb(skb)->ctx->req); kfree(ovpn_skb_cb(skb)->ctx); @@ -133,6 +137,10 @@ void ovpn_decrypt_post(void *data, int ret) goto drop; } + /* increment RX stats */ + ovpn_peer_stats_increment_rx(&peer->vpn_stats, skb->len); + ovpn_peer_stats_increment_rx(&peer->link_stats, orig_len); + ovpn_netdev_write(peer, skb); /* skb is passed to upper layer - don't free it */ skb = NULL; @@ -171,6 +179,7 @@ void ovpn_encrypt_post(void *data, int ret) { struct ovpn_peer *peer = NULL; struct sk_buff *skb = data; + unsigned int orig_len = 0; /* encryption is happening asynchronously. This function will be * called later by the crypto callback with a proper return value @@ -181,6 +190,7 @@ void ovpn_encrypt_post(void *data, int ret) /* crypto is done, cleanup skb CB and its members */ if (likely(ovpn_skb_cb(skb)->ctx)) { peer = ovpn_skb_cb(skb)->ctx->peer; + orig_len = ovpn_skb_cb(skb)->ctx->orig_len; ovpn_crypto_key_slot_put(ovpn_skb_cb(skb)->ctx->ks); aead_request_free(ovpn_skb_cb(skb)->ctx->req); @@ -192,6 +202,8 @@ void ovpn_encrypt_post(void *data, int ret) goto err; skb_mark_not_on_list(skb); + ovpn_peer_stats_increment_tx(&peer->link_stats, skb->len); + ovpn_peer_stats_increment_tx(&peer->vpn_stats, orig_len); switch (peer->sock->sock->sk->sk_protocol) { case IPPROTO_UDP: diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 212d1210bc564ae804899d7c3bf8c2231e732cf1..6d34c56a4a51ce407b20b6175e61
[PATCH net-next v8 13/24] ovpn: implement TCP transport
With this change ovpn is allowed to communicate to peers also via TCP. Parsing of incoming messages is implemented through the strparser API. Signed-off-by: Antonio Quartulli --- drivers/net/Kconfig | 1 + drivers/net/ovpn/Makefile | 1 + drivers/net/ovpn/io.c | 4 + drivers/net/ovpn/main.c | 3 + drivers/net/ovpn/peer.h | 37 drivers/net/ovpn/socket.c | 27 ++- drivers/net/ovpn/socket.h | 7 +- drivers/net/ovpn/tcp.c| 506 ++ drivers/net/ovpn/tcp.h| 43 9 files changed, 625 insertions(+), 4 deletions(-) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0055bcd2356c70bd9cfd8a944f549817dab8154f..cef1dd4dccad37c7b60a00aa41ab258c8df0807c 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -118,6 +118,7 @@ config WIREGUARD_DEBUG config OVPN tristate "OpenVPN data channel offload" depends on NET && INET + select STREAM_PARSER select NET_UDP_TUNNEL select DST_CACHE select CRYPTO diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index d43fda72646bdc7644d9a878b56da0a0e5680c98..f4d4bd87c851c8dd5b81e357315c4b22de4bd092 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -18,4 +18,5 @@ ovpn-y += peer.o ovpn-y += pktid.o ovpn-y += socket.o ovpn-y += stats.o +ovpn-y += tcp.o ovpn-y += udp.o diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index d54af1a0f03a33b03001a041e62554b35a7f5ebc..985f8bba6f1355b9f164e53f11575fa104133d43 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -22,6 +22,7 @@ #include "netlink.h" #include "proto.h" #include "socket.h" +#include "tcp.h" #include "udp.h" #include "skb.h" @@ -209,6 +210,9 @@ void ovpn_encrypt_post(void *data, int ret) case IPPROTO_UDP: ovpn_udp_send_skb(peer->ovpn, peer, skb); break; + case IPPROTO_TCP: + ovpn_tcp_send_skb(peer, skb); + break; default: /* no transport configured yet */ goto err; diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 670e66f8f750086de5d48e4708721d102c149a2e..cb5cc3d4b5620f5a1e401e06e52e67c32e57aa78 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -25,6 +25,7 @@ #include "io.h" #include "packet.h" #include "peer.h" +#include "tcp.h" /* Driver info */ #define DRV_DESCRIPTION"OpenVPN data channel offload (ovpn)" @@ -270,6 +271,8 @@ static int __init ovpn_init(void) goto unreg_rtnl; } + ovpn_tcp_init(); + return 0; unreg_rtnl: diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index 89f6face71871e09f8efd533a949f0d9358a1342..86d4696b1529c45025b63d2973fc48ca81ca8f63 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -11,6 +11,7 @@ #define _NET_OVPN_OVPNPEER_H_ #include +#include #include #include "bind.h" @@ -30,6 +31,18 @@ * @vpn_addrs.ipv4: IPv4 assigned to peer on the tunnel * @vpn_addrs.ipv6: IPv6 assigned to peer on the tunnel * @sock: the socket being used to talk to this peer + * @tcp: keeps track of TCP specific state + * @tcp.strp: stream parser context (TCP only) + * @tcp.tx_work: work for deferring outgoing packet processing (TCP only) + * @tcp.user_queue: received packets that have to go to userspace (TCP only) + * @tcp.tx_in_progress: true if TX is already ongoing (TCP only) + * @tcp.out_msg.skb: packet scheduled for sending (TCP only) + * @tcp.out_msg.offset: offset where next send should start (TCP only) + * @tcp.out_msg.len: remaining data to send within packet (TCP only) + * @tcp.sk_cb.sk_data_ready: pointer to original cb (TCP only) + * @tcp.sk_cb.sk_write_space: pointer to original cb (TCP only) + * @tcp.sk_cb.prot: pointer to original prot object (TCP only) + * @tcp.sk_cb.ops: pointer to the original prot_ops object (TCP only) * @crypto: the crypto configuration (ciphers, keys, etc..) * @dst_cache: cache for dst_entry used to send to peer * @bind: remote peer binding @@ -50,6 +63,30 @@ struct ovpn_peer { struct in6_addr ipv6; } vpn_addrs; struct ovpn_socket *sock; + + /* state of the TCP reading. Needed to keep track of how much of a +* single packet has already been read from the stream and how much is +* missing +*/ + struct { + struct strparser strp; + struct work_struct tx_work; + struct sk_buff_head user_queue; + bool tx_in_progress; + + struct { + struct sk_buff *skb; + int offset; + int len; + } out_msg; + + struct { + void (
[PATCH net-next v8 14/24] ovpn: implement multi-peer support
With this change an ovpn instance will be able to stay connected to multiple remote endpoints. This functionality is strictly required when running ovpn on an OpenVPN server. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 50 +++- drivers/net/ovpn/ovpnstruct.h | 19 + drivers/net/ovpn/peer.c | 173 -- drivers/net/ovpn/peer.h | 9 +++ 4 files changed, 241 insertions(+), 10 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index cb5cc3d4b5620f5a1e401e06e52e67c32e57aa78..7604f0970d3c283f8680f6800a125427999bb174 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -50,13 +50,53 @@ static void ovpn_struct_free(struct net_device *net) struct ovpn_struct *ovpn = netdev_priv(net); gro_cells_destroy(&ovpn->gro_cells); + kfree(ovpn->peers); } static int ovpn_net_init(struct net_device *dev) { struct ovpn_struct *ovpn = netdev_priv(dev); + struct in_device *dev_v4; + int i, err; - return gro_cells_init(&ovpn->gro_cells, dev); + err = gro_cells_init(&ovpn->gro_cells, dev); + if (err) + return err; + + if (ovpn->mode == OVPN_MODE_MP) { + dev_v4 = __in_dev_get_rtnl(dev); + if (dev_v4) { + /* disable redirects as Linux gets confused by ovpn +* handling same-LAN routing. +* This happens because a multipeer interface is used as +* relay point between hosts in the same subnet, while +* in a classic LAN this would not be needed because the +* two hosts would be able to talk directly. +*/ + IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false); + IPV4_DEVCONF_ALL(dev_net(dev), SEND_REDIRECTS) = false; + } + + /* the peer container is fairly large, therefore we dynamically +* allocate it only when needed +*/ + ovpn->peers = kzalloc(sizeof(*ovpn->peers), GFP_KERNEL); + if (!ovpn->peers) { + gro_cells_destroy(&ovpn->gro_cells); + return -ENOMEM; + } + + spin_lock_init(&ovpn->peers->lock); + + for (i = 0; i < ARRAY_SIZE(ovpn->peers->by_id); i++) { + INIT_HLIST_HEAD(&ovpn->peers->by_id[i]); + INIT_HLIST_NULLS_HEAD(&ovpn->peers->by_vpn_addr[i], i); + INIT_HLIST_NULLS_HEAD(&ovpn->peers->by_transp_addr[i], + i); + } + } + + return 0; } static int ovpn_net_open(struct net_device *dev) @@ -201,8 +241,14 @@ void ovpn_iface_destruct(struct ovpn_struct *ovpn) ovpn->registered = false; - if (ovpn->mode == OVPN_MODE_P2P) + switch (ovpn->mode) { + case OVPN_MODE_P2P: ovpn_peer_release_p2p(ovpn); + break; + default: + ovpn_peers_free(ovpn); + break; + } } static int ovpn_netdev_notifier_call(struct notifier_block *nb, diff --git a/drivers/net/ovpn/ovpnstruct.h b/drivers/net/ovpn/ovpnstruct.h index 65497ce115aa4be719ed03f89135a80b73f9ef9b..4373d1bbc4874dc2f1d1f1697e7b847ec2f486c9 100644 --- a/drivers/net/ovpn/ovpnstruct.h +++ b/drivers/net/ovpn/ovpnstruct.h @@ -14,6 +14,23 @@ #include #include +/** + * struct ovpn_peer_collection - container of peers for MultiPeer mode + * @by_id: table of peers index by ID + * @by_vpn_addr: table of peers indexed by VPN IP address (items can be + * rehashed on the fly due to peer IP change) + * @by_transp_addr: table of peers indexed by transport address (items can be + * rehashed on the fly due to peer IP change) + * @lock: protects writes to peer tables + */ +struct ovpn_peer_collection { + DECLARE_HASHTABLE(by_id, 12); + struct hlist_nulls_head by_vpn_addr[1 << 12]; + struct hlist_nulls_head by_transp_addr[1 << 12]; + + spinlock_t lock; /* protects writes to peer tables */ +}; + /** * struct ovpn_struct - per ovpn interface state * @dev: the actual netdev representing the tunnel @@ -21,6 +38,7 @@ * @registered: whether dev is still registered with netdev or not * @mode: device operation mode (i.e. p2p, mp, ..) * @lock: protect this object + * @peers: data structures holding multi-peer references * @peer: in P2P mode, this is the only remote peer * @dev_list: entry for the module wide device list * @gro_cells: pointer to the Generic Receive Offload cell @@ -31,6 +49,7 @@ struct ovpn_struct { bool registered; enum ovpn_mode mode;
[PATCH net-next v8 15/24] ovpn: implement peer lookup logic
In a multi-peer scenario there are a number of situations when a specific peer needs to be looked up. We may want to lookup a peer by: 1. its ID 2. its VPN destination IP 3. its transport IP/port couple For each of the above, there is a specific routing table referencing all peers for fast look up. Case 2. is a bit special in the sense that an outgoing packet may not be sent to the peer VPN IP directly, but rather to a network behind it. For this reason we first perform a nexthop lookup in the system routing table and then we use the retrieved nexthop as peer search key. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 272 ++-- 1 file changed, 264 insertions(+), 8 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 3c6fbf99f696c18d8a2dbe169c7f6f5933fb71ac..54d0a416f6f91513f42d5ecc1f1f65d688f3e908 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "ovpnstruct.h" #include "bind.h" @@ -126,6 +127,94 @@ static bool ovpn_peer_skb_to_sockaddr(struct sk_buff *skb, return true; } +/** + * ovpn_nexthop_from_skb4 - retrieve IPv4 nexthop for outgoing skb + * @skb: the outgoing packet + * + * Return: the IPv4 of the nexthop + */ +static __be32 ovpn_nexthop_from_skb4(struct sk_buff *skb) +{ + const struct rtable *rt = skb_rtable(skb); + + if (rt && rt->rt_uses_gateway) + return rt->rt_gw4; + + return ip_hdr(skb)->daddr; +} + +/** + * ovpn_nexthop_from_skb6 - retrieve IPv6 nexthop for outgoing skb + * @skb: the outgoing packet + * + * Return: the IPv6 of the nexthop + */ +static struct in6_addr ovpn_nexthop_from_skb6(struct sk_buff *skb) +{ + const struct rt6_info *rt = skb_rt6_info(skb); + + if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) + return ipv6_hdr(skb)->daddr; + + return rt->rt6i_gateway; +} + +#define ovpn_get_hash_head(_tbl, _key, _key_len) ({\ + typeof(_tbl) *__tbl = &(_tbl); \ + (&(*__tbl)[jhash(_key, _key_len, 0) % HASH_SIZE(*__tbl)]); }) \ + +/** + * ovpn_peer_get_by_vpn_addr4 - retrieve peer by its VPN IPv4 address + * @ovpn: the openvpn instance to search + * @addr: VPN IPv4 to use as search key + * + * Refcounter is not increased for the returned peer. + * + * Return: the peer if found or NULL otherwise + */ +static struct ovpn_peer *ovpn_peer_get_by_vpn_addr4(struct ovpn_struct *ovpn, + __be32 addr) +{ + struct hlist_nulls_head *nhead; + struct hlist_nulls_node *ntmp; + struct ovpn_peer *tmp; + + nhead = ovpn_get_hash_head(ovpn->peers->by_vpn_addr, &addr, + sizeof(addr)); + + hlist_nulls_for_each_entry_rcu(tmp, ntmp, nhead, hash_entry_addr4) + if (addr == tmp->vpn_addrs.ipv4.s_addr) + return tmp; + + return NULL; +} + +/** + * ovpn_peer_get_by_vpn_addr6 - retrieve peer by its VPN IPv6 address + * @ovpn: the openvpn instance to search + * @addr: VPN IPv6 to use as search key + * + * Refcounter is not increased for the returned peer. + * + * Return: the peer if found or NULL otherwise + */ +static struct ovpn_peer *ovpn_peer_get_by_vpn_addr6(struct ovpn_struct *ovpn, + struct in6_addr *addr) +{ + struct hlist_nulls_head *nhead; + struct hlist_nulls_node *ntmp; + struct ovpn_peer *tmp; + + nhead = ovpn_get_hash_head(ovpn->peers->by_vpn_addr, addr, + sizeof(*addr)); + + hlist_nulls_for_each_entry_rcu(tmp, ntmp, nhead, hash_entry_addr6) + if (ipv6_addr_equal(addr, &tmp->vpn_addrs.ipv6)) + return tmp; + + return NULL; +} + /** * ovpn_peer_transp_match - check if sockaddr and peer binding match * @peer: the peer to get the binding from @@ -203,14 +292,44 @@ ovpn_peer_get_by_transp_addr_p2p(struct ovpn_struct *ovpn, struct ovpn_peer *ovpn_peer_get_by_transp_addr(struct ovpn_struct *ovpn, struct sk_buff *skb) { - struct ovpn_peer *peer = NULL; + struct ovpn_peer *tmp, *peer = NULL; struct sockaddr_storage ss = { 0 }; + struct hlist_nulls_head *nhead; + struct hlist_nulls_node *ntmp; + size_t sa_len; if (unlikely(!ovpn_peer_skb_to_sockaddr(skb, &ss))) return NULL; if (ovpn->mode == OVPN_MODE_P2P) - peer = ovpn_peer_get_by_transp_addr_p2p(ovpn, &ss); + return ovpn_peer_get_by_transp_addr_p2p(ovpn, &ss); + + switch (ss.ss_family) { + case AF_INET: + sa_len = sizeof(struct sockaddr_in); + break; + case AF_
[PATCH net-next v8 16/24] ovpn: implement keepalive mechanism
OpenVPN supports configuring a periodic keepalive packet. message to allow the remote endpoint detect link failures. This change implements the keepalive sending and timer expiring logic. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 77 + drivers/net/ovpn/io.h | 5 ++ drivers/net/ovpn/main.c | 3 + drivers/net/ovpn/ovpnstruct.h | 2 + drivers/net/ovpn/peer.c | 188 ++ drivers/net/ovpn/peer.h | 15 drivers/net/ovpn/proto.h | 2 - 7 files changed, 290 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 985f8bba6f1355b9f164e53f11575fa104133d43..4e69f31382d2cb9ce4bc40f06cfbae47add5b5ba 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -26,6 +26,33 @@ #include "udp.h" #include "skb.h" +const unsigned char ovpn_keepalive_message[OVPN_KEEPALIVE_SIZE] = { + 0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb, + 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48 +}; + +/** + * ovpn_is_keepalive - check if skb contains a keepalive message + * @skb: packet to check + * + * Assumes that the first byte of skb->data is defined. + * + * Return: true if skb contains a keepalive or false otherwise + */ +static bool ovpn_is_keepalive(struct sk_buff *skb) +{ + if (*skb->data != ovpn_keepalive_message[0]) + return false; + + if (skb->len != OVPN_KEEPALIVE_SIZE) + return false; + + if (!pskb_may_pull(skb, OVPN_KEEPALIVE_SIZE)) + return false; + + return !memcmp(skb->data, ovpn_keepalive_message, OVPN_KEEPALIVE_SIZE); +} + /* Called after decrypt to write the IP packet to the device. * This method is expected to manage/free the skb. */ @@ -103,6 +130,9 @@ void ovpn_decrypt_post(void *data, int ret) goto drop; } + /* keep track of last received authenticated packet for keepalive */ + peer->last_recv = ktime_get_real_seconds(); + /* point to encapsulated IP packet */ __skb_pull(skb, payload_offset); @@ -119,6 +149,12 @@ void ovpn_decrypt_post(void *data, int ret) goto drop; } + if (ovpn_is_keepalive(skb)) { + net_dbg_ratelimited("%s: ping received from peer %u\n", + peer->ovpn->dev->name, peer->id); + goto drop; + } + net_info_ratelimited("%s: unsupported protocol received from peer %u\n", peer->ovpn->dev->name, peer->id); goto drop; @@ -217,6 +253,10 @@ void ovpn_encrypt_post(void *data, int ret) /* no transport configured yet */ goto err; } + + /* keep track of last sent packet for keepalive */ + peer->last_sent = ktime_get_real_seconds(); + /* skb passed down the stack - don't free it */ skb = NULL; err: @@ -355,3 +395,40 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) kfree_skb_list(skb); return NET_XMIT_DROP; } + +/** + * ovpn_xmit_special - encrypt and transmit an out-of-band message to peer + * @peer: peer to send the message to + * @data: message content + * @len: message length + * + * Assumes that caller holds a reference to peer + */ +void ovpn_xmit_special(struct ovpn_peer *peer, const void *data, + const unsigned int len) +{ + struct ovpn_struct *ovpn; + struct sk_buff *skb; + + ovpn = peer->ovpn; + if (unlikely(!ovpn)) + return; + + skb = alloc_skb(256 + len, GFP_ATOMIC); + if (unlikely(!skb)) + return; + + skb_reserve(skb, 128); + skb->priority = TC_PRIO_BESTEFFORT; + __skb_put_data(skb, data, len); + + /* increase reference counter when passing peer to sending queue */ + if (!ovpn_peer_hold(peer)) { + netdev_dbg(ovpn->dev, "%s: cannot hold peer reference for sending special packet\n", + __func__); + kfree_skb(skb); + return; + } + + ovpn_send(ovpn, skb, peer); +} diff --git a/drivers/net/ovpn/io.h b/drivers/net/ovpn/io.h index ad81dd86924689309b3299573575a1705eddaf99..eb224114152c29f42aadf026212e8d278006b490 100644 --- a/drivers/net/ovpn/io.h +++ b/drivers/net/ovpn/io.h @@ -10,9 +10,14 @@ #ifndef _NET_OVPN_OVPN_H_ #define _NET_OVPN_OVPN_H_ +#define OVPN_KEEPALIVE_SIZE 16 +extern const unsigned char ovpn_keepalive_message[OVPN_KEEPALIVE_SIZE]; + netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev); void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb); +void ovpn_xmit_special(struct ovpn_peer *peer, const void *data, +
[PATCH net-next v8 18/24] ovpn: add support for peer floating
A peer connected via UDP may change its IP address without reconnecting (float). Add support for detecting and updating the new peer IP/port in case of floating. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/bind.c | 10 ++-- drivers/net/ovpn/io.c | 9 drivers/net/ovpn/peer.c | 128 ++-- drivers/net/ovpn/peer.h | 2 + 4 files changed, 138 insertions(+), 11 deletions(-) diff --git a/drivers/net/ovpn/bind.c b/drivers/net/ovpn/bind.c index b4d2ccec2ceddf43bc445b489cc62a578ef0ad0a..d17d078c5730bf4336dc87f45cdba3f6b8cad770 100644 --- a/drivers/net/ovpn/bind.c +++ b/drivers/net/ovpn/bind.c @@ -47,12 +47,8 @@ struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss) * @new: the new bind to assign */ void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *new) + __must_hold(&peer->lock) { - struct ovpn_bind *old; - - spin_lock_bh(&peer->lock); - old = rcu_replace_pointer(peer->bind, new, true); - spin_unlock_bh(&peer->lock); - - kfree_rcu(old, rcu); + kfree_rcu(rcu_replace_pointer(peer->bind, new, + lockdep_is_held(&peer->lock)), rcu); } diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 4e69f31382d2cb9ce4bc40f06cfbae47add5b5ba..8f2b4a85d20fbdb512de7ec312d391985e96b906 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -133,6 +133,15 @@ void ovpn_decrypt_post(void *data, int ret) /* keep track of last received authenticated packet for keepalive */ peer->last_recv = ktime_get_real_seconds(); + if (peer->sock->sock->sk->sk_protocol == IPPROTO_UDP) { + /* check if this peer changed it's IP address and update +* state +*/ + ovpn_peer_float(peer, skb); + /* update source endpoint for this peer */ + ovpn_peer_update_local_endpoint(peer, skb); + } + /* point to encapsulated IP packet */ __skb_pull(skb, payload_offset); diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index f9d8f1d1827fe67dc4b4e0bba41a5b110bb90819..891cf2fa6a81b46ac764ad4fd50d4456fa7ce5bd 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -94,6 +94,128 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_struct *ovpn, u32 id) return peer; } +/** + * ovpn_peer_reset_sockaddr - recreate binding for peer + * @peer: peer to recreate the binding for + * @ss: sockaddr to use as remote endpoint for the binding + * @local_ip: local IP for the binding + * + * Return: 0 on success or a negative error code otherwise + */ +static int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, + const struct sockaddr_storage *ss, + const u8 *local_ip) + __must_hold(&peer->lock) +{ + struct ovpn_bind *bind; + size_t ip_len; + + /* create new ovpn_bind object */ + bind = ovpn_bind_from_sockaddr(ss); + if (IS_ERR(bind)) + return PTR_ERR(bind); + + if (local_ip) { + if (ss->ss_family == AF_INET) { + ip_len = sizeof(struct in_addr); + } else if (ss->ss_family == AF_INET6) { + ip_len = sizeof(struct in6_addr); + } else { + netdev_dbg(peer->ovpn->dev, "%s: invalid family for remote endpoint\n", + __func__); + kfree(bind); + return -EINVAL; + } + + memcpy(&bind->local, local_ip, ip_len); + } + + /* set binding */ + ovpn_bind_reset(peer, bind); + + return 0; +} + +#define ovpn_get_hash_head(_tbl, _key, _key_len) ({\ + typeof(_tbl) *__tbl = &(_tbl); \ + (&(*__tbl)[jhash(_key, _key_len, 0) % HASH_SIZE(*__tbl)]); }) \ + +/** + * ovpn_peer_float - update remote endpoint for peer + * @peer: peer to update the remote endpoint for + * @skb: incoming packet to retrieve the source address (remote) from + */ +void ovpn_peer_float(struct ovpn_peer *peer, struct sk_buff *skb) +{ + struct hlist_nulls_head *nhead; + struct sockaddr_storage ss; + const u8 *local_ip = NULL; + struct sockaddr_in6 *sa6; + struct sockaddr_in *sa; + struct ovpn_bind *bind; + sa_family_t family; + size_t salen; + + rcu_read_lock(); + bind = rcu_dereference(peer->bind); + if (unlikely(!bind)) { + rcu_read_unlock(); + return; + } + + spin_lock_bh(&peer->lock); + if (likely(ovpn_bind_skb_src_match(bind, skb))) + goto unlock; + + family = skb_protocol_to_family(skb); + + if (bind->remote.in
[PATCH net-next v8 17/24] ovpn: add support for updating local UDP endpoint
In case of UDP links, the local endpoint used to communicate with a given peer may change without a connection restart. Add support for learning the new address in case of change. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 45 + drivers/net/ovpn/peer.h | 3 +++ 2 files changed, 48 insertions(+) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index dfaa4c7c012215b3dcd451b9bd1d7ffce9b1291c..f9d8f1d1827fe67dc4b4e0bba41a5b110bb90819 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -418,6 +418,51 @@ struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_struct *ovpn, u32 peer_id) return peer; } +/** + * ovpn_peer_update_local_endpoint - update local endpoint for peer + * @peer: peer to update the endpoint for + * @skb: incoming packet to retrieve the destination address (local) from + */ +void ovpn_peer_update_local_endpoint(struct ovpn_peer *peer, +struct sk_buff *skb) +{ + struct ovpn_bind *bind; + + rcu_read_lock(); + bind = rcu_dereference(peer->bind); + if (unlikely(!bind)) + goto unlock; + + spin_lock_bh(&peer->lock); + switch (skb_protocol_to_family(skb)) { + case AF_INET: + if (unlikely(bind->local.ipv4.s_addr != ip_hdr(skb)->daddr)) { + netdev_dbg(peer->ovpn->dev, + "%s: learning local IPv4 for peer %d (%pI4 -> %pI4)\n", + __func__, peer->id, &bind->local.ipv4.s_addr, + &ip_hdr(skb)->daddr); + bind->local.ipv4.s_addr = ip_hdr(skb)->daddr; + } + break; + case AF_INET6: + if (unlikely(!ipv6_addr_equal(&bind->local.ipv6, + &ipv6_hdr(skb)->daddr))) { + netdev_dbg(peer->ovpn->dev, + "%s: learning local IPv6 for peer %d (%pI6c -> %pI6c\n", + __func__, peer->id, &bind->local.ipv6, + &ipv6_hdr(skb)->daddr); + bind->local.ipv6 = ipv6_hdr(skb)->daddr; + } + break; + default: + break; + } + spin_unlock_bh(&peer->lock); + +unlock: + rcu_read_unlock(); +} + /** * ovpn_peer_get_by_dst - Lookup peer to send skb to * @ovpn: the private data representing the current VPN session diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index e0d8d1255b837b7ddfdcedc5a56e7e747f13e2c5..6b66e169b33510a794f8f43dff757f0357e3e5de 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -159,4 +159,7 @@ bool ovpn_peer_check_by_src(struct ovpn_struct *ovpn, struct sk_buff *skb, void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout); void ovpn_peer_keepalive_work(struct work_struct *work); +void ovpn_peer_update_local_endpoint(struct ovpn_peer *peer, +struct sk_buff *skb); + #endif /* _NET_OVPN_OVPNPEER_H_ */ -- 2.45.2
[PATCH net-next v8 21/24] ovpn: kill key and notify userspace in case of IV exhaustion
IV wrap-around is cryptographically dangerous for a number of ciphers, therefore kill the key and inform userspace (via netlink) should the IV space go exhausted. Userspace has two ways of deciding when the key has to be renewed before exhausting the IV space: 1) time based approach: after X seconds/minutes userspace generates a new key and sends it to the kernel. This is based on guestimate and normally default timer value works well. 2) packet count based approach: after X packets/bytes userspace generates a new key and sends it to the kernel. Userspace keeps track of the amount of traffic by periodically polling GET_PEER and fetching the VPN/LINK stats. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/crypto.c | 19 drivers/net/ovpn/crypto.h | 2 ++ drivers/net/ovpn/io.c | 18 ++- drivers/net/ovpn/netlink.c | 55 ++ drivers/net/ovpn/netlink.h | 2 ++ 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c index f1f7510e2f735e367f96eb4982ba82c9af3c8bfc..56ae2f5e5c33e13933f26acd91c7786f7c02011a 100644 --- a/drivers/net/ovpn/crypto.c +++ b/drivers/net/ovpn/crypto.c @@ -55,6 +55,25 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs) } } +/* removes the key matching the specified id from the crypto context */ +void ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id) +{ + struct ovpn_crypto_key_slot *ks = NULL; + + spin_lock_bh(&cs->lock); + if (rcu_access_pointer(cs->slots[0])->key_id == key_id) { + ks = rcu_replace_pointer(cs->slots[0], NULL, +lockdep_is_held(&cs->lock)); + } else if (rcu_access_pointer(cs->slots[1])->key_id == key_id) { + ks = rcu_replace_pointer(cs->slots[1], NULL, +lockdep_is_held(&cs->lock)); + } + spin_unlock_bh(&cs->lock); + + if (ks) + ovpn_crypto_key_slot_put(ks); +} + /* Reset the ovpn_crypto_state object in a way that is atomic * to RCU readers. */ diff --git a/drivers/net/ovpn/crypto.h b/drivers/net/ovpn/crypto.h index 61de855d23649598a6e5a34447a35ed9620dfde1..48ea6ae9f52114a2f55d71304243d52467ebeef5 100644 --- a/drivers/net/ovpn/crypto.h +++ b/drivers/net/ovpn/crypto.h @@ -133,4 +133,6 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs); void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs); +void ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id); + #endif /* _NET_OVPN_OVPNCRYPTO_H_ */ diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 8f2b4a85d20fbdb512de7ec312d391985e96b906..3e82ff882d00b92b67e320f2521269b6bd731083 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -223,6 +223,7 @@ void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb) void ovpn_encrypt_post(void *data, int ret) { + struct ovpn_crypto_key_slot *ks = NULL; struct ovpn_peer *peer = NULL; struct sk_buff *skb = data; unsigned int orig_len = 0; @@ -235,15 +236,28 @@ void ovpn_encrypt_post(void *data, int ret) /* crypto is done, cleanup skb CB and its members */ if (likely(ovpn_skb_cb(skb)->ctx)) { + ks = ovpn_skb_cb(skb)->ctx->ks; peer = ovpn_skb_cb(skb)->ctx->peer; orig_len = ovpn_skb_cb(skb)->ctx->orig_len; - ovpn_crypto_key_slot_put(ovpn_skb_cb(skb)->ctx->ks); aead_request_free(ovpn_skb_cb(skb)->ctx->req); kfree(ovpn_skb_cb(skb)->ctx); ovpn_skb_cb(skb)->ctx = NULL; } + if (unlikely(ret == -ERANGE)) { + /* we ran out of IVs and we must kill the key as it can't be +* use anymore +*/ + netdev_warn(peer->ovpn->dev, + "killing key %u for peer %u\n", ks->key_id, + peer->id); + ovpn_crypto_kill_key(&peer->crypto, ks->key_id); + /* let userspace know so that a new key must be negotiated */ + ovpn_nl_key_swap_notify(peer, ks->key_id); + goto err; + } + if (unlikely(ret < 0)) goto err; @@ -273,6 +287,8 @@ void ovpn_encrypt_post(void *data, int ret) dev_core_stats_tx_dropped_inc(peer->ovpn->dev); if (likely(peer)) ovpn_peer_put(peer); + if (likely(ks)) + ovpn_crypto_key_slot_put(ks); kfree_skb(skb); } diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index c9cede9025505d1b69ec8eeeb60420d0a1647c82..e9cf847e99026e78eee4d65b62911671e8c2e407 100644 --- a/drivers/net/ovpn/netlink.c +++ b/d
[PATCH net-next v8 22/24] ovpn: notify userspace when a peer is deleted
Whenever a peer is deleted, send a notification to userspace so that it can react accordingly. This is most important when a peer is deleted due to ping timeout, because it all happens in kernelspace and thus userspace has no direct way to learn about it. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 55 ++ drivers/net/ovpn/netlink.h | 1 + drivers/net/ovpn/peer.c| 1 + 3 files changed, 57 insertions(+) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index e9cf847e99026e78eee4d65b62911671e8c2e407..b96c84fcc24921b2cd173ec2b6358b59eec72b39 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -963,6 +963,61 @@ int ovpn_nl_key_del_doit(struct sk_buff *skb, struct genl_info *info) return 0; } +/** + * ovpn_nl_peer_del_notify - notify userspace about peer being deleted + * @peer: the peer being deleted + * + * Return: 0 on success or a negative error code otherwise + */ +int ovpn_nl_peer_del_notify(struct ovpn_peer *peer) +{ + struct sk_buff *msg; + struct nlattr *attr; + int ret = -EMSGSIZE; + void *hdr; + + netdev_info(peer->ovpn->dev, "deleting peer with id %u, reason %d\n", + peer->id, peer->delete_reason); + + msg = nlmsg_new(100, GFP_ATOMIC); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &ovpn_nl_family, 0, OVPN_CMD_PEER_DEL_NTF); + if (!hdr) { + ret = -ENOBUFS; + goto err_free_msg; + } + + if (nla_put_u32(msg, OVPN_A_IFINDEX, peer->ovpn->dev->ifindex)) + goto err_cancel_msg; + + attr = nla_nest_start(msg, OVPN_A_PEER); + if (!attr) + goto err_cancel_msg; + + if (nla_put_u8(msg, OVPN_A_PEER_DEL_REASON, peer->delete_reason)) + goto err_cancel_msg; + + if (nla_put_u32(msg, OVPN_A_PEER_ID, peer->id)) + goto err_cancel_msg; + + nla_nest_end(msg, attr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(&ovpn_nl_family, dev_net(peer->ovpn->dev), msg, + 0, OVPN_NLGRP_PEERS, GFP_ATOMIC); + + return 0; + +err_cancel_msg: + genlmsg_cancel(msg, hdr); +err_free_msg: + nlmsg_free(msg); + return ret; +} + /** * ovpn_nl_key_swap_notify - notify userspace peer's key must be renewed * @peer: the peer whose key needs to be renewed diff --git a/drivers/net/ovpn/netlink.h b/drivers/net/ovpn/netlink.h index 33390b13c8904d40b629662005a9eb92ff617c3b..4ab3abcf23dba11f6b92e3d69e700693adbc671b 100644 --- a/drivers/net/ovpn/netlink.h +++ b/drivers/net/ovpn/netlink.h @@ -12,6 +12,7 @@ int ovpn_nl_register(void); void ovpn_nl_unregister(void); +int ovpn_nl_peer_del_notify(struct ovpn_peer *peer); int ovpn_nl_key_swap_notify(struct ovpn_peer *peer, u8 key_id); #endif /* _NET_OVPN_NETLINK_H_ */ diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 1ce31f0317cb3593a8edf95c43d03a0bddb0a58f..f201bcddb2d792e4f6b759d5e1fed42234aa1410 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -249,6 +249,7 @@ void ovpn_peer_release_kref(struct kref *kref) if (peer->sock) ovpn_socket_put(peer->sock); + ovpn_nl_peer_del_notify(peer); call_rcu(&peer->rcu, ovpn_peer_release_rcu); } -- 2.45.2
[PATCH net-next v8 19/24] ovpn: implement peer add/dump/delete via netlink
This change introduces the netlink command needed to add, delete and retrieve/dump known peers. Userspace is expected to use these commands to handle known peer lifecycles. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 582 - drivers/net/ovpn/peer.c| 66 +++-- drivers/net/ovpn/peer.h| 5 + 3 files changed, 625 insertions(+), 28 deletions(-) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 6e60591d605dde19c6bbd47ef0e90e522776688c..29ff6023f9135b81a1d5fa42354398c70008aee5 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -17,6 +18,10 @@ #include "io.h" #include "netlink.h" #include "netlink-gen.h" +#include "bind.h" +#include "packet.h" +#include "peer.h" +#include "socket.h" MODULE_ALIAS_GENL_FAMILY(OVPN_FAMILY_NAME); @@ -147,29 +152,596 @@ int ovpn_nl_dev_del_doit(struct sk_buff *skb, struct genl_info *info) return 0; } +static int ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs, + struct sockaddr_storage *ss) +{ + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; + struct in6_addr *in6; + __be16 port = 0; + __be32 *in; + int af; + + ss->ss_family = AF_UNSPEC; + + if (attrs[OVPN_A_PEER_REMOTE_PORT]) + port = nla_get_be16(attrs[OVPN_A_PEER_REMOTE_PORT]); + + if (attrs[OVPN_A_PEER_REMOTE_IPV4]) { + af = AF_INET; + ss->ss_family = AF_INET; + in = nla_data(attrs[OVPN_A_PEER_REMOTE_IPV4]); + } else if (attrs[OVPN_A_PEER_REMOTE_IPV6]) { + af = AF_INET6; + ss->ss_family = AF_INET6; + in6 = nla_data(attrs[OVPN_A_PEER_REMOTE_IPV6]); + } else { + return AF_UNSPEC; + } + + switch (ss->ss_family) { + case AF_INET6: + /* If this is a regular IPv6 just break and move on, +* otherwise switch to AF_INET and extract the IPv4 accordingly +*/ + if (!ipv6_addr_v4mapped(in6)) { + sin6 = (struct sockaddr_in6 *)ss; + sin6->sin6_port = port; + memcpy(&sin6->sin6_addr, in6, sizeof(*in6)); + break; + } + + /* v4-mapped-v6 address */ + ss->ss_family = AF_INET; + in = &in6->s6_addr32[3]; + fallthrough; + case AF_INET: + sin = (struct sockaddr_in *)ss; + sin->sin_port = port; + sin->sin_addr.s_addr = *in; + break; + } + + /* don't return ss->ss_family as it may have changed in case of +* v4-mapped-v6 address +*/ + return af; +} + +static u8 *ovpn_nl_attr_local_ip(struct nlattr **attrs) +{ + u8 *addr6; + + if (!attrs[OVPN_A_PEER_LOCAL_IPV4] && !attrs[OVPN_A_PEER_LOCAL_IPV6]) + return NULL; + + if (attrs[OVPN_A_PEER_LOCAL_IPV4]) + return nla_data(attrs[OVPN_A_PEER_LOCAL_IPV4]); + + addr6 = nla_data(attrs[OVPN_A_PEER_LOCAL_IPV6]); + /* this is an IPv4-mapped IPv6 address, therefore extract the actual +* v4 address from the last 4 bytes +*/ + if (ipv6_addr_v4mapped((struct in6_addr *)addr6)) + return addr6 + 12; + + return addr6; +} + +static int ovpn_nl_peer_precheck(struct ovpn_struct *ovpn, +struct genl_info *info, +struct nlattr **attrs) +{ + if (NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_PEER], attrs, + OVPN_A_PEER_ID)) + return -EINVAL; + + if (attrs[OVPN_A_PEER_REMOTE_IPV4] && attrs[OVPN_A_PEER_REMOTE_IPV6]) { + NL_SET_ERR_MSG_MOD(info->extack, + "cannot specify both remote IPv4 or IPv6 address"); + return -EINVAL; + } + + if (!attrs[OVPN_A_PEER_REMOTE_IPV4] && + !attrs[OVPN_A_PEER_REMOTE_IPV6] && attrs[OVPN_A_PEER_REMOTE_PORT]) { + NL_SET_ERR_MSG_MOD(info->extack, + "cannot specify remote port without IP address"); + return -EINVAL; + } + + if (!attrs[OVPN_A_PEER_REMOTE_IPV4] && + attrs[OVPN_A_PEER_LOCAL_IPV4]) { + NL_SET_ERR_MSG_MOD(info->extack, + "cannot specify local IPv4 address without remote"); + return -EINVAL; + } + + if (!attrs[OVPN_A_PEER_REMOTE_IPV6] && + attrs[OVPN_A_PEE
[PATCH net-next v8 20/24] ovpn: implement key add/del/swap via netlink
This change introduces the netlink commands needed to add, delete and swap keys for a specific peer. Userspace is expected to use these commands to create, destroy and rotate session keys for a specific peer. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 210 - 1 file changed, 207 insertions(+), 3 deletions(-) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 29ff6023f9135b81a1d5fa42354398c70008aee5..c9cede9025505d1b69ec8eeeb60420d0a1647c82 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -744,19 +744,223 @@ int ovpn_nl_peer_del_doit(struct sk_buff *skb, struct genl_info *info) return ret; } +static int ovpn_nl_get_key_dir(struct genl_info *info, struct nlattr *key, + enum ovpn_cipher_alg cipher, + struct ovpn_key_direction *dir) +{ + struct nlattr *attrs[OVPN_A_KEYDIR_MAX + 1]; + int ret; + + ret = nla_parse_nested(attrs, OVPN_A_KEYDIR_MAX, key, + ovpn_keydir_nl_policy, info->extack); + if (ret) + return ret; + + switch (cipher) { + case OVPN_CIPHER_ALG_AES_GCM: + case OVPN_CIPHER_ALG_CHACHA20_POLY1305: + if (NL_REQ_ATTR_CHECK(info->extack, key, attrs, + OVPN_A_KEYDIR_CIPHER_KEY) || + NL_REQ_ATTR_CHECK(info->extack, key, attrs, + OVPN_A_KEYDIR_NONCE_TAIL)) + return -EINVAL; + + dir->cipher_key = nla_data(attrs[OVPN_A_KEYDIR_CIPHER_KEY]); + dir->cipher_key_size = nla_len(attrs[OVPN_A_KEYDIR_CIPHER_KEY]); + + /* These algorithms require a 96bit nonce, +* Construct it by combining 4-bytes packet id and +* 8-bytes nonce-tail from userspace +*/ + dir->nonce_tail = nla_data(attrs[OVPN_A_KEYDIR_NONCE_TAIL]); + dir->nonce_tail_size = nla_len(attrs[OVPN_A_KEYDIR_NONCE_TAIL]); + break; + default: + NL_SET_ERR_MSG_MOD(info->extack, "unsupported cipher"); + return -EINVAL; + } + + return 0; +} + +/** + * ovpn_nl_key_new_doit - configure a new key for the specified peer + * @skb: incoming netlink message + * @info: genetlink metadata + * + * This function allows the user to install a new key in the peer crypto + * state. + * Each peer has two 'slots', namely 'primary' and 'secondary', where + * keys can be installed. The key in the 'primary' slot is used for + * encryption, while both keys can be used for decryption by matching the + * key ID carried in the incoming packet. + * + * The user is responsible for rotating keys when necessary. The user + * may fetch peer traffic statistics via netlink in order to better + * identify the right time to rotate keys. + * The renegotiation follows these steps: + * 1. a new key is computed by the user and is installed in the 'secondary' + *slot + * 2. at user discretion (usually after a predetermined time) 'primary' and + *'secondary' contents are swapped and the new key starts being used for + *encryption, while the old key is kept around for decryption of late + *packets. + * + * Return: 0 on success or a negative error code otherwise. + */ int ovpn_nl_key_new_doit(struct sk_buff *skb, struct genl_info *info) { - return -EOPNOTSUPP; + struct nlattr *attrs[OVPN_A_KEYCONF_MAX + 1]; + struct ovpn_struct *ovpn = info->user_ptr[0]; + struct ovpn_peer_key_reset pkr; + struct ovpn_peer *peer; + u32 peer_id; + int ret; + + if (GENL_REQ_ATTR_CHECK(info, OVPN_A_KEYCONF)) + return -EINVAL; + + ret = nla_parse_nested(attrs, OVPN_A_KEYCONF_MAX, + info->attrs[OVPN_A_KEYCONF], + ovpn_keyconf_nl_policy, info->extack); + if (ret) + return ret; + + if (NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs, + OVPN_A_KEYCONF_PEER_ID)) + return -EINVAL; + + if (NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs, + OVPN_A_KEYCONF_SLOT) || + NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs, + OVPN_A_KEYCONF_KEY_ID) || + NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs, + OVPN_A_KEYCONF_CIPHER_ALG) || + NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_KEYCONF], attrs, + OVPN_A_KEYCONF_ENCRYPT_DIR) || + NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVP
[PATCH net-next v8 24/24] testing/selftest: add test tool and scripts for ovpn module
The ovpn-cli tool can be compiled and used as selftest for the ovpn kernel module. It implementes the netlink API and can thus be integrated in any script for more automated testing. Along with the tool, 2 scripts are added that perform basic functionality tests by means of network namespaces. The scripts can be performed in sequence by running run.sh Cc: sh...@kernel.org Cc: linux-kselft...@vger.kernel.org Signed-off-by: Antonio Quartulli --- MAINTAINERS |1 + tools/testing/selftests/Makefile |1 + tools/testing/selftests/net/ovpn/.gitignore |2 + tools/testing/selftests/net/ovpn/Makefile | 18 + tools/testing/selftests/net/ovpn/config |8 + tools/testing/selftests/net/ovpn/data-test-tcp.sh |9 + tools/testing/selftests/net/ovpn/data-test.sh | 153 ++ tools/testing/selftests/net/ovpn/data64.key |5 + tools/testing/selftests/net/ovpn/float-test.sh| 118 ++ tools/testing/selftests/net/ovpn/ovpn-cli.c | 1822 + tools/testing/selftests/net/ovpn/tcp_peers.txt|5 + tools/testing/selftests/net/ovpn/udp_peers.txt|5 + 12 files changed, 2147 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f753060d4e2467a786778ddd4f835861a603ce02..ffd997cc6a1f1fbc5bc954b585bc15ef7bf2486a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17457,6 +17457,7 @@ T: git https://github.com/OpenVPN/linux-kernel-ovpn.git F: drivers/net/ovpn/ F: include/uapi/linux/ovpn.h F: Documentation/netlink/spec/ovpn.yaml +F: tools/testing/selftests/net/ovpn/ P54 WIRELESS DRIVER M: Christian Lamparter diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index b38199965f99014f3e2636fe8d705972f2c0d148..3ae2dd6492ca70d5e317c6e5b4e2560b060e3214 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -68,6 +68,7 @@ TARGETS += net/hsr TARGETS += net/mptcp TARGETS += net/netfilter TARGETS += net/openvswitch +TARGETS += net/ovpn TARGETS += net/packetdrill TARGETS += net/rds TARGETS += net/tcp_ao diff --git a/tools/testing/selftests/net/ovpn/.gitignore b/tools/testing/selftests/net/ovpn/.gitignore new file mode 100644 index ..ee44c081ca7c089933659689303c303a9fa9713b --- /dev/null +++ b/tools/testing/selftests/net/ovpn/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0+ +ovpn-cli diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile new file mode 100644 index ..65e23eb0ba86d31aa365b08a8c3468dc56a0d1a4 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020-2024 OpenVPN, Inc. +# +CFLAGS = -Wall -I../../../../../usr/include +CFLAGS += $(shell pkg-config --cflags libnl-3.0 libnl-genl-3.0) + +LDFLAGS = -lmbedtls -lmbedcrypto +LDFLAGS += $(shell pkg-config --libs libnl-3.0 libnl-genl-3.0) + +ovpn-cli: ovpn-cli.c + $(CC) -o ovpn-cli ovpn-cli.c $(CFLAGS) $(LDFLAGS) + +TEST_PROGS = data-test.sh \ + data-test-tcp.sh \ + float-test.sh +TEST_GEN_FILES = ovpn-cli + +include ../../lib.mk diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config new file mode 100644 index ..5ff47de23c1297a310f0fe39a1ebad16ad014d2c --- /dev/null +++ b/tools/testing/selftests/net/ovpn/config @@ -0,0 +1,8 @@ +CONFIG_NET=y +CONFIG_INET=y +CONFIG_NET_UDP_TUNNEL=y +CONFIG_DST_CACHE=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_OVPN=y diff --git a/tools/testing/selftests/net/ovpn/data-test-tcp.sh b/tools/testing/selftests/net/ovpn/data-test-tcp.sh new file mode 100755 index ..65f05659b5fd8f05d216f2c22898e7f89a6ea4de --- /dev/null +++ b/tools/testing/selftests/net/ovpn/data-test-tcp.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2024 OpenVPN, Inc. +# +# Author: Antonio Quartulli + +PROTO="TCP" + +source data-test.sh diff --git a/tools/testing/selftests/net/ovpn/data-test.sh b/tools/testing/selftests/net/ovpn/data-test.sh new file mode 100755 index ..e3401481440165141993767cc94fd6df7a37a52e --- /dev/null +++ b/tools/testing/selftests/net/ovpn/data-test.sh @@ -0,0 +1,153 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020-2024 OpenVPN, Inc. +# +# Author: Antonio Quartulli + +#set -x +set -e + +UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt} +TCP_PEERS_FILE=${TCP_PEERS_FILE:-tcp_peers.txt} +OVPN_CLI=${OVPN_CLI:-./ovpn-cli} +ALG=${ALG:-aes} +PROTO=${PROTO:-UDP} + +create_ns() { + ip netns add peer${1} +} + +setup_ns() { + MODE="P2P" + + if [ ${1} -eq 0 ]; then + MODE=&quo
[PATCH net-next v8 23/24] ovpn: add basic ethtool support
Implement support for basic ethtool functionality. Note that ovpn is a virtual device driver, therefore various ethtool APIs are just not meaningful and thus not implemented. Signed-off-by: Antonio Quartulli Reviewed-by: Andrew Lunn --- drivers/net/ovpn/main.c | 15 +++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 6048df5890c8fa46f3d91498903b4277a33f06db..a164b6ca2a4b8d69a76938fcd1b76d1f2e563dd8 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -7,6 +7,7 @@ * James Yonan */ +#include #include #include #include @@ -137,6 +138,19 @@ bool ovpn_dev_is_valid(const struct net_device *dev) return dev->netdev_ops->ndo_start_xmit == ovpn_net_xmit; } +static void ovpn_get_drvinfo(struct net_device *dev, +struct ethtool_drvinfo *info) +{ + strscpy(info->driver, OVPN_FAMILY_NAME, sizeof(info->driver)); + strscpy(info->bus_info, "ovpn", sizeof(info->bus_info)); +} + +static const struct ethtool_ops ovpn_ethtool_ops = { + .get_drvinfo= ovpn_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ts_info= ethtool_op_get_ts_info, +}; + static struct rtnl_link_ops ovpn_link_ops = { .kind = OVPN_FAMILY_NAME, .netns_refund = false, @@ -158,6 +172,7 @@ static void ovpn_setup(struct net_device *dev) dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + dev->ethtool_ops = &ovpn_ethtool_ops; dev->netdev_ops = &ovpn_netdev_ops; dev->rtnl_link_ops = &ovpn_link_ops; -- 2.45.2
[PATCH net-next v11 12/23] ovpn: implement TCP transport
With this change ovpn is allowed to communicate to peers also via TCP. Parsing of incoming messages is implemented through the strparser API. Signed-off-by: Antonio Quartulli --- drivers/net/Kconfig | 1 + drivers/net/ovpn/Makefile | 1 + drivers/net/ovpn/io.c | 4 + drivers/net/ovpn/main.c | 3 + drivers/net/ovpn/peer.h | 37 drivers/net/ovpn/socket.c | 44 +++- drivers/net/ovpn/socket.h | 9 +- drivers/net/ovpn/tcp.c| 506 ++ drivers/net/ovpn/tcp.h| 44 9 files changed, 643 insertions(+), 6 deletions(-) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 269b73fcfd348a48174fb96b8f8d4f8788636fa8..f37ce285e61fbee3201f4095ada3230305df511b 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -118,6 +118,7 @@ config WIREGUARD_DEBUG config OVPN tristate "OpenVPN data channel offload" depends on NET && INET + select STREAM_PARSER select NET_UDP_TUNNEL select DST_CACHE select CRYPTO diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index d43fda72646bdc7644d9a878b56da0a0e5680c98..f4d4bd87c851c8dd5b81e357315c4b22de4bd092 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -18,4 +18,5 @@ ovpn-y += peer.o ovpn-y += pktid.o ovpn-y += socket.o ovpn-y += stats.o +ovpn-y += tcp.o ovpn-y += udp.o diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index d56e74660c7be9020b5bdf7971322d41afd436d6..deda19ab87391f86964ba43088b7847d22420eee 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -22,6 +22,7 @@ #include "crypto_aead.h" #include "netlink.h" #include "proto.h" +#include "tcp.h" #include "udp.h" #include "skb.h" #include "socket.h" @@ -213,6 +214,9 @@ void ovpn_encrypt_post(void *data, int ret) case IPPROTO_UDP: ovpn_udp_send_skb(peer->ovpn, peer, skb); break; + case IPPROTO_TCP: + ovpn_tcp_send_skb(peer, skb); + break; default: /* no transport configured yet */ goto err; diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 73348765a8cf24321aa6be78e75f607d6dbffb1d..0488e395eb27d3dba1efc8ff39c023e0ac4a38dd 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -22,6 +22,7 @@ #include "io.h" #include "packet.h" #include "peer.h" +#include "tcp.h" /* Driver info */ #define DRV_DESCRIPTION"OpenVPN data channel offload (ovpn)" @@ -237,6 +238,8 @@ static int __init ovpn_init(void) goto unreg_rtnl; } + ovpn_tcp_init(); + return 0; unreg_rtnl: diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index eb1e31e854fbfff25d07fba8026789e41a76c113..2b7fa9510e362ef3646157bb0d361bab19ddaa99 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -11,6 +11,7 @@ #define _NET_OVPN_OVPNPEER_H_ #include +#include #include "crypto.h" #include "stats.h" @@ -23,6 +24,18 @@ * @vpn_addrs.ipv4: IPv4 assigned to peer on the tunnel * @vpn_addrs.ipv6: IPv6 assigned to peer on the tunnel * @sock: the socket being used to talk to this peer + * @tcp: keeps track of TCP specific state + * @tcp.strp: stream parser context (TCP only) + * @tcp.tx_work: work for deferring outgoing packet processing (TCP only) + * @tcp.user_queue: received packets that have to go to userspace (TCP only) + * @tcp.tx_in_progress: true if TX is already ongoing (TCP only) + * @tcp.out_msg.skb: packet scheduled for sending (TCP only) + * @tcp.out_msg.offset: offset where next send should start (TCP only) + * @tcp.out_msg.len: remaining data to send within packet (TCP only) + * @tcp.sk_cb.sk_data_ready: pointer to original cb (TCP only) + * @tcp.sk_cb.sk_write_space: pointer to original cb (TCP only) + * @tcp.sk_cb.prot: pointer to original prot object (TCP only) + * @tcp.sk_cb.ops: pointer to the original prot_ops object (TCP only) * @crypto: the crypto configuration (ciphers, keys, etc..) * @dst_cache: cache for dst_entry used to send to peer * @bind: remote peer binding @@ -43,6 +56,30 @@ struct ovpn_peer { struct in6_addr ipv6; } vpn_addrs; struct ovpn_socket *sock; + + /* state of the TCP reading. Needed to keep track of how much of a +* single packet has already been read from the stream and how much is +* missing +*/ + struct { + struct strparser strp; + struct work_struct tx_work; + struct sk_buff_head user_queue; + bool tx_in_progress; + + struct { + struct sk_buff *skb; + int offset; + int len; + } o
[PATCH net-next v11 14/23] ovpn: implement peer lookup logic
In a multi-peer scenario there are a number of situations when a specific peer needs to be looked up. We may want to lookup a peer by: 1. its ID 2. its VPN destination IP 3. its transport IP/port couple For each of the above, there is a specific routing table referencing all peers for fast look up. Case 2. is a bit special in the sense that an outgoing packet may not be sent to the peer VPN IP directly, but rather to a network behind it. For this reason we first perform a nexthop lookup in the system routing table and then we use the retrieved nexthop as peer search key. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 272 ++-- 1 file changed, 264 insertions(+), 8 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 73ef509faab9701192a45ffe78a46dbbbeab01c2..c7dc9032c2b55fd42befc1f3e7a0eca893a96576 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "ovpnstruct.h" #include "bind.h" @@ -125,6 +126,94 @@ static bool ovpn_peer_skb_to_sockaddr(struct sk_buff *skb, return true; } +/** + * ovpn_nexthop_from_skb4 - retrieve IPv4 nexthop for outgoing skb + * @skb: the outgoing packet + * + * Return: the IPv4 of the nexthop + */ +static __be32 ovpn_nexthop_from_skb4(struct sk_buff *skb) +{ + const struct rtable *rt = skb_rtable(skb); + + if (rt && rt->rt_uses_gateway) + return rt->rt_gw4; + + return ip_hdr(skb)->daddr; +} + +/** + * ovpn_nexthop_from_skb6 - retrieve IPv6 nexthop for outgoing skb + * @skb: the outgoing packet + * + * Return: the IPv6 of the nexthop + */ +static struct in6_addr ovpn_nexthop_from_skb6(struct sk_buff *skb) +{ + const struct rt6_info *rt = skb_rt6_info(skb); + + if (!rt || !(rt->rt6i_flags & RTF_GATEWAY)) + return ipv6_hdr(skb)->daddr; + + return rt->rt6i_gateway; +} + +#define ovpn_get_hash_head(_tbl, _key, _key_len) ({\ + typeof(_tbl) *__tbl = &(_tbl); \ + (&(*__tbl)[jhash(_key, _key_len, 0) % HASH_SIZE(*__tbl)]); }) \ + +/** + * ovpn_peer_get_by_vpn_addr4 - retrieve peer by its VPN IPv4 address + * @ovpn: the openvpn instance to search + * @addr: VPN IPv4 to use as search key + * + * Refcounter is not increased for the returned peer. + * + * Return: the peer if found or NULL otherwise + */ +static struct ovpn_peer *ovpn_peer_get_by_vpn_addr4(struct ovpn_struct *ovpn, + __be32 addr) +{ + struct hlist_nulls_head *nhead; + struct hlist_nulls_node *ntmp; + struct ovpn_peer *tmp; + + nhead = ovpn_get_hash_head(ovpn->peers->by_vpn_addr, &addr, + sizeof(addr)); + + hlist_nulls_for_each_entry_rcu(tmp, ntmp, nhead, hash_entry_addr4) + if (addr == tmp->vpn_addrs.ipv4.s_addr) + return tmp; + + return NULL; +} + +/** + * ovpn_peer_get_by_vpn_addr6 - retrieve peer by its VPN IPv6 address + * @ovpn: the openvpn instance to search + * @addr: VPN IPv6 to use as search key + * + * Refcounter is not increased for the returned peer. + * + * Return: the peer if found or NULL otherwise + */ +static struct ovpn_peer *ovpn_peer_get_by_vpn_addr6(struct ovpn_struct *ovpn, + struct in6_addr *addr) +{ + struct hlist_nulls_head *nhead; + struct hlist_nulls_node *ntmp; + struct ovpn_peer *tmp; + + nhead = ovpn_get_hash_head(ovpn->peers->by_vpn_addr, addr, + sizeof(*addr)); + + hlist_nulls_for_each_entry_rcu(tmp, ntmp, nhead, hash_entry_addr6) + if (ipv6_addr_equal(addr, &tmp->vpn_addrs.ipv6)) + return tmp; + + return NULL; +} + /** * ovpn_peer_transp_match - check if sockaddr and peer binding match * @peer: the peer to get the binding from @@ -202,14 +291,44 @@ ovpn_peer_get_by_transp_addr_p2p(struct ovpn_struct *ovpn, struct ovpn_peer *ovpn_peer_get_by_transp_addr(struct ovpn_struct *ovpn, struct sk_buff *skb) { - struct ovpn_peer *peer = NULL; + struct ovpn_peer *tmp, *peer = NULL; struct sockaddr_storage ss = { 0 }; + struct hlist_nulls_head *nhead; + struct hlist_nulls_node *ntmp; + size_t sa_len; if (unlikely(!ovpn_peer_skb_to_sockaddr(skb, &ss))) return NULL; if (ovpn->mode == OVPN_MODE_P2P) - peer = ovpn_peer_get_by_transp_addr_p2p(ovpn, &ss); + return ovpn_peer_get_by_transp_addr_p2p(ovpn, &ss); + + switch (ss.ss_family) { + case AF_INET: + sa_len = sizeof(struct sockaddr_in); + break; + case AF_
[PATCH net-next v11 01/23] netlink: add NLA_POLICY_MAX_LEN macro
Similarly to NLA_POLICY_MIN_LEN, NLA_POLICY_MAX_LEN defines a policy with a maximum length value. The netlink generator for YAML specs has been extended accordingly. Signed-off-by: Antonio Quartulli Reviewed-by: Donald Hunter --- include/net/netlink.h | 1 + tools/net/ynl/ynl-gen-c.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/net/netlink.h b/include/net/netlink.h index db6af207287c839408c58cb28b82408e0548eaca..2dc671c977ff3297975269d236264907009703d3 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -469,6 +469,7 @@ struct nla_policy { .max = _len \ } #define NLA_POLICY_MIN_LEN(_len) NLA_POLICY_MIN(NLA_BINARY, _len) +#define NLA_POLICY_MAX_LEN(_len) NLA_POLICY_MAX(NLA_BINARY, _len) /** * struct nl_info - netlink source information diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py index 1a825b4081b222cf97eb73f01a2a5c1ffe47cd5c..aa22eb0924754f38ea0b9e68a1ff5a55d94d6717 100755 --- a/tools/net/ynl/ynl-gen-c.py +++ b/tools/net/ynl/ynl-gen-c.py @@ -481,7 +481,7 @@ class TypeBinary(Type): pass elif len(self.checks) == 1: check_name = list(self.checks)[0] -if check_name not in {'exact-len', 'min-len'}: +if check_name not in {'exact-len', 'min-len', 'max-len'}: raise Exception('Unsupported check for binary type: ' + check_name) else: raise Exception('More than one check for binary type not implemented, yet') @@ -492,6 +492,8 @@ class TypeBinary(Type): mem = 'NLA_POLICY_EXACT_LEN(' + self.get_limit_str('exact-len') + ')' elif 'min-len' in self.checks: mem = '{ .len = ' + self.get_limit_str('min-len') + ', }' +elif 'max-len' in self.checks: +mem = 'NLA_POLICY_MAX_LEN(' + self.get_limit_str('max-len') + ')' return mem -- 2.45.2
[PATCH net-next v11 23/23] testing/selftests: add test tool and scripts for ovpn module
The ovpn-cli tool can be compiled and used as selftest for the ovpn kernel module. [NOTE: it depends on libmedtls for decoding base64-encoded keys] ovpn-cli implements the netlink and RTNL APIs and can thus be integrated in any script for more automated testing. Along with the tool, 4 scripts are provided that perform basic functionality tests by means of network namespaces. These scripts take part to the kselftest automation. The output of the tool, which will appear in the kselftest reports, is a list of steps performed by the scripts plus some output coming from the execution of `ping` and `iperf`. In general it is useful only in case of failure, in order to understand which step has failed and why. Cc: linux-kselft...@vger.kernel.org Signed-off-by: Antonio Quartulli Reviewed-by: Shuah Khan --- MAINTAINERS|1 + tools/testing/selftests/Makefile |1 + tools/testing/selftests/net/ovpn/.gitignore|2 + tools/testing/selftests/net/ovpn/Makefile | 17 + tools/testing/selftests/net/ovpn/config| 10 + tools/testing/selftests/net/ovpn/data64.key|5 + tools/testing/selftests/net/ovpn/ovpn-cli.c| 2370 tools/testing/selftests/net/ovpn/tcp_peers.txt |5 + .../testing/selftests/net/ovpn/test-chachapoly.sh |9 + tools/testing/selftests/net/ovpn/test-float.sh |9 + tools/testing/selftests/net/ovpn/test-tcp.sh |9 + tools/testing/selftests/net/ovpn/test.sh | 183 ++ tools/testing/selftests/net/ovpn/udp_peers.txt |5 + 13 files changed, 2626 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index cf3d55c3e98aaea8f8817faed99dd7499cd59a71..110485aec73ae5bfeef4f228490ed76e28e01870 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17295,6 +17295,7 @@ T: git https://github.com/OpenVPN/linux-kernel-ovpn.git F: Documentation/netlink/specs/ovpn.yaml F: drivers/net/ovpn/ F: include/uapi/linux/ovpn.h +F: tools/testing/selftests/net/ovpn/ OPENVSWITCH M: Pravin B Shelar diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 363d031a16f7e14152c904e6b68dab1f90c98392..be42906ecb11d4b0f9866d2c04b0e8fb27a2b995 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -68,6 +68,7 @@ TARGETS += net/hsr TARGETS += net/mptcp TARGETS += net/netfilter TARGETS += net/openvswitch +TARGETS += net/ovpn TARGETS += net/packetdrill TARGETS += net/rds TARGETS += net/tcp_ao diff --git a/tools/testing/selftests/net/ovpn/.gitignore b/tools/testing/selftests/net/ovpn/.gitignore new file mode 100644 index ..ee44c081ca7c089933659689303c303a9fa9713b --- /dev/null +++ b/tools/testing/selftests/net/ovpn/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0+ +ovpn-cli diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile new file mode 100644 index ..c76d8fd953c5674941c8c2787813063b1bce180f --- /dev/null +++ b/tools/testing/selftests/net/ovpn/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020-2024 OpenVPN, Inc. +# +CFLAGS = -pedantic -Wextra -Wall -Wl,--no-as-needed -g -O0 -ggdb $(KHDR_INCLUDES) +CFLAGS += $(shell pkg-config --cflags libnl-3.0 libnl-genl-3.0) + +LDFLAGS = -lmbedtls -lmbedcrypto +LDFLAGS += $(shell pkg-config --libs libnl-3.0 libnl-genl-3.0) + +TEST_PROGS = test.sh \ + test-chachapoly.sh \ + test-tcp.sh \ + test-float.sh + +TEST_GEN_FILES = ovpn-cli + +include ../../lib.mk diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config new file mode 100644 index ..71946ba9fa175c191725e369eb9b973503d9d9c4 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/config @@ -0,0 +1,10 @@ +CONFIG_NET=y +CONFIG_INET=y +CONFIG_STREAM_PARSER=y +CONFIG_NET_UDP_TUNNEL=y +CONFIG_DST_CACHE=y +CONFIG_CRYPTO=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CHACHA20POLY1305=y +CONFIG_OVPN=m diff --git a/tools/testing/selftests/net/ovpn/data64.key b/tools/testing/selftests/net/ovpn/data64.key new file mode 100644 index ..a99e88c4e290f58b12f399b857b873f308d9ba09 --- /dev/null +++ b/tools/testing/selftests/net/ovpn/data64.key @@ -0,0 +1,5 @@ +jRqMACN7d7/aFQNT8S7jkrBD8uwrgHbG5OQZP2eu4R1Y7tfpS2bf5RHv06Vi163CGoaIiTX99R3B +ia9ycAH8Wz1+9PWv51dnBLur9jbShlgZ2QHLtUc4a/gfT7zZwULXuuxdLnvR21DDeMBaTbkgbai9 +uvAa7ne1liIgGFzbv+Bas4HDVrygxIxuAnP5Qgc3648IJkZ0QEXPF+O9f0n5+QIvGCxkAUVx+5K6 +KIs+SoeWXnAopELmoGSjUpFtJbagXK82HfdqpuUxT2Tnuef0/14SzVE/vNleBNu2ZbyrSAaah8tE +BofkPJUBFY+YQcfZNM5Dgrw3i+Bpmpq/gpdg5w== diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c new file mode 100644 index
[PATCH net-next v11 13/23] ovpn: implement multi-peer support
With this change an ovpn instance will be able to stay connected to multiple remote endpoints. This functionality is strictly required when running ovpn on an OpenVPN server. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 55 +- drivers/net/ovpn/ovpnstruct.h | 19 + drivers/net/ovpn/peer.c | 166 -- drivers/net/ovpn/peer.h | 9 +++ 4 files changed, 243 insertions(+), 6 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 0488e395eb27d3dba1efc8ff39c023e0ac4a38dd..c7453127ab640d7268c1ce919a87cc5419fac9ee 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -30,6 +30,9 @@ static void ovpn_struct_free(struct net_device *net) { + struct ovpn_struct *ovpn = netdev_priv(net); + + kfree(ovpn->peers); } static int ovpn_net_init(struct net_device *dev) @@ -133,12 +136,52 @@ static void ovpn_setup(struct net_device *dev) SET_NETDEV_DEVTYPE(dev, &ovpn_type); } +static int ovpn_mp_alloc(struct ovpn_struct *ovpn) +{ + struct in_device *dev_v4; + int i; + + if (ovpn->mode != OVPN_MODE_MP) + return 0; + + dev_v4 = __in_dev_get_rtnl(ovpn->dev); + if (dev_v4) { + /* disable redirects as Linux gets confused by ovpn +* handling same-LAN routing. +* This happens because a multipeer interface is used as +* relay point between hosts in the same subnet, while +* in a classic LAN this would not be needed because the +* two hosts would be able to talk directly. +*/ + IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false); + IPV4_DEVCONF_ALL(dev_net(ovpn->dev), SEND_REDIRECTS) = false; + } + + /* the peer container is fairly large, therefore we allocate it only in +* MP mode +*/ + ovpn->peers = kzalloc(sizeof(*ovpn->peers), GFP_KERNEL); + if (!ovpn->peers) + return -ENOMEM; + + spin_lock_init(&ovpn->peers->lock); + + for (i = 0; i < ARRAY_SIZE(ovpn->peers->by_id); i++) { + INIT_HLIST_HEAD(&ovpn->peers->by_id[i]); + INIT_HLIST_NULLS_HEAD(&ovpn->peers->by_vpn_addr[i], i); + INIT_HLIST_NULLS_HEAD(&ovpn->peers->by_transp_addr[i], i); + } + + return 0; +} + static int ovpn_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { struct ovpn_struct *ovpn = netdev_priv(dev); enum ovpn_mode mode = OVPN_MODE_P2P; + int err; if (data && data[IFLA_OVPN_MODE]) { mode = nla_get_u8(data[IFLA_OVPN_MODE]); @@ -149,6 +192,10 @@ static int ovpn_newlink(struct net *src_net, struct net_device *dev, ovpn->mode = mode; spin_lock_init(&ovpn->lock); + err = ovpn_mp_alloc(ovpn); + if (err < 0) + return err; + /* turn carrier explicitly off after registration, this way state is * clearly defined */ @@ -197,8 +244,14 @@ static int ovpn_netdev_notifier_call(struct notifier_block *nb, netif_carrier_off(dev); ovpn->registered = false; - if (ovpn->mode == OVPN_MODE_P2P) + switch (ovpn->mode) { + case OVPN_MODE_P2P: ovpn_peer_release_p2p(ovpn); + break; + case OVPN_MODE_MP: + ovpn_peers_free(ovpn); + break; + } break; case NETDEV_POST_INIT: case NETDEV_GOING_DOWN: diff --git a/drivers/net/ovpn/ovpnstruct.h b/drivers/net/ovpn/ovpnstruct.h index 4a48fc048890ab1cda78bc104fe3034b4a49d226..12ed5e22c2108c9f143d1984048eb40c887cac63 100644 --- a/drivers/net/ovpn/ovpnstruct.h +++ b/drivers/net/ovpn/ovpnstruct.h @@ -15,6 +15,23 @@ #include #include +/** + * struct ovpn_peer_collection - container of peers for MultiPeer mode + * @by_id: table of peers index by ID + * @by_vpn_addr: table of peers indexed by VPN IP address (items can be + * rehashed on the fly due to peer IP change) + * @by_transp_addr: table of peers indexed by transport address (items can be + * rehashed on the fly due to peer IP change) + * @lock: protects writes to peer tables + */ +struct ovpn_peer_collection { + DECLARE_HASHTABLE(by_id, 12); + struct hlist_nulls_head by_vpn_addr[1 << 12]; + struct hlist_nulls_head by_transp_addr[1 << 12]; + + spinlock_t lock; /* protects writes to peer tables */ +}; + /** * struct ovpn_struct - per ovpn interface state * @dev: the actual netdev representing the tunn
[PATCH net-next v11 17/23] ovpn: add support for peer floating
A peer connected via UDP may change its IP address without reconnecting (float). Add support for detecting and updating the new peer IP/port in case of floating. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/bind.c | 10 ++-- drivers/net/ovpn/io.c | 9 drivers/net/ovpn/peer.c | 129 ++-- drivers/net/ovpn/peer.h | 2 + 4 files changed, 139 insertions(+), 11 deletions(-) diff --git a/drivers/net/ovpn/bind.c b/drivers/net/ovpn/bind.c index b4d2ccec2ceddf43bc445b489cc62a578ef0ad0a..d17d078c5730bf4336dc87f45cdba3f6b8cad770 100644 --- a/drivers/net/ovpn/bind.c +++ b/drivers/net/ovpn/bind.c @@ -47,12 +47,8 @@ struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss) * @new: the new bind to assign */ void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *new) + __must_hold(&peer->lock) { - struct ovpn_bind *old; - - spin_lock_bh(&peer->lock); - old = rcu_replace_pointer(peer->bind, new, true); - spin_unlock_bh(&peer->lock); - - kfree_rcu(old, rcu); + kfree_rcu(rcu_replace_pointer(peer->bind, new, + lockdep_is_held(&peer->lock)), rcu); } diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 63c140138bf98e5d1df79a2565b666d86513323d..0e8a6f2c76bc7b2ccc287ad1187cf50f033bf261 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -135,6 +135,15 @@ void ovpn_decrypt_post(void *data, int ret) /* keep track of last received authenticated packet for keepalive */ peer->last_recv = ktime_get_real_seconds(); + if (peer->sock->sock->sk->sk_protocol == IPPROTO_UDP) { + /* check if this peer changed it's IP address and update +* state +*/ + ovpn_peer_float(peer, skb); + /* update source endpoint for this peer */ + ovpn_peer_update_local_endpoint(peer, skb); + } + /* point to encapsulated IP packet */ __skb_pull(skb, payload_offset); diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 3f67d200e283213fcb732d10f9edeb53e0a0e9ee..da6215bbb643592e4567e61e4b4976d367ed109c 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -94,6 +94,131 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_struct *ovpn, u32 id) return peer; } +/** + * ovpn_peer_reset_sockaddr - recreate binding for peer + * @peer: peer to recreate the binding for + * @ss: sockaddr to use as remote endpoint for the binding + * @local_ip: local IP for the binding + * + * Return: 0 on success or a negative error code otherwise + */ +static int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, + const struct sockaddr_storage *ss, + const u8 *local_ip) + __must_hold(&peer->lock) +{ + struct ovpn_bind *bind; + size_t ip_len; + + /* create new ovpn_bind object */ + bind = ovpn_bind_from_sockaddr(ss); + if (IS_ERR(bind)) + return PTR_ERR(bind); + + if (local_ip) { + if (ss->ss_family == AF_INET) { + ip_len = sizeof(struct in_addr); + } else if (ss->ss_family == AF_INET6) { + ip_len = sizeof(struct in6_addr); + } else { + netdev_dbg(peer->ovpn->dev, "%s: invalid family for remote endpoint\n", + __func__); + kfree(bind); + return -EINVAL; + } + + memcpy(&bind->local, local_ip, ip_len); + } + + /* set binding */ + ovpn_bind_reset(peer, bind); + + return 0; +} + +#define ovpn_get_hash_head(_tbl, _key, _key_len) ({\ + typeof(_tbl) *__tbl = &(_tbl); \ + (&(*__tbl)[jhash(_key, _key_len, 0) % HASH_SIZE(*__tbl)]); }) \ + +/** + * ovpn_peer_float - update remote endpoint for peer + * @peer: peer to update the remote endpoint for + * @skb: incoming packet to retrieve the source address (remote) from + */ +void ovpn_peer_float(struct ovpn_peer *peer, struct sk_buff *skb) +{ + struct hlist_nulls_head *nhead; + struct sockaddr_storage ss; + const u8 *local_ip = NULL; + struct sockaddr_in6 *sa6; + struct sockaddr_in *sa; + struct ovpn_bind *bind; + sa_family_t family; + size_t salen; + + rcu_read_lock(); + bind = rcu_dereference(peer->bind); + if (unlikely(!bind)) { + rcu_read_unlock(); + return; + } + + spin_lock_bh(&peer->lock); + if (likely(ovpn_bind_skb_src_match(bind, skb))) + goto unlock; + + family = skb_protocol_to_family(skb); + + if (bind->remote.in
[PATCH net-next v11 04/23] ovpn: add basic interface creation/destruction/management routines
Add basic infrastructure for handling ovpn interfaces. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 115 -- drivers/net/ovpn/main.h | 7 +++ drivers/net/ovpn/ovpnstruct.h | 8 +++ drivers/net/ovpn/packet.h | 40 +++ include/uapi/linux/if_link.h | 15 ++ 5 files changed, 180 insertions(+), 5 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index d5bdb0055f4dd3a6e32dc6e792bed1e7fd59e101..eead7677b8239eb3c48bb26ca95492d88512b8d4 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -10,18 +10,52 @@ #include #include #include +#include +#include #include -#include +#include #include "ovpnstruct.h" #include "main.h" #include "netlink.h" #include "io.h" +#include "packet.h" /* Driver info */ #define DRV_DESCRIPTION"OpenVPN data channel offload (ovpn)" #define DRV_COPYRIGHT "(C) 2020-2024 OpenVPN, Inc." +static void ovpn_struct_free(struct net_device *net) +{ +} + +static int ovpn_net_open(struct net_device *dev) +{ + netif_tx_start_all_queues(dev); + return 0; +} + +static int ovpn_net_stop(struct net_device *dev) +{ + netif_tx_stop_all_queues(dev); + return 0; +} + +static const struct net_device_ops ovpn_netdev_ops = { + .ndo_open = ovpn_net_open, + .ndo_stop = ovpn_net_stop, + .ndo_start_xmit = ovpn_net_xmit, +}; + +static const struct device_type ovpn_type = { + .name = OVPN_FAMILY_NAME, +}; + +static const struct nla_policy ovpn_policy[IFLA_OVPN_MAX + 1] = { + [IFLA_OVPN_MODE] = NLA_POLICY_RANGE(NLA_U8, OVPN_MODE_P2P, + OVPN_MODE_MP), +}; + /** * ovpn_dev_is_valid - check if the netdevice is of type 'ovpn' * @dev: the interface to check @@ -33,16 +67,76 @@ bool ovpn_dev_is_valid(const struct net_device *dev) return dev->netdev_ops->ndo_start_xmit == ovpn_net_xmit; } +static void ovpn_setup(struct net_device *dev) +{ + /* compute the overhead considering AEAD encryption */ + const int overhead = sizeof(u32) + NONCE_WIRE_SIZE + 16 + +sizeof(struct udphdr) + +max(sizeof(struct ipv6hdr), sizeof(struct iphdr)); + + netdev_features_t feat = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM | +NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | +NETIF_F_HIGHDMA; + + dev->needs_free_netdev = true; + + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + + dev->netdev_ops = &ovpn_netdev_ops; + + dev->priv_destructor = ovpn_struct_free; + + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->mtu = ETH_DATA_LEN - overhead; + dev->min_mtu = IPV4_MIN_MTU; + dev->max_mtu = IP_MAX_MTU - overhead; + + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->priv_flags |= IFF_NO_QUEUE; + + dev->lltx = true; + dev->features |= feat; + dev->hw_features |= feat; + dev->hw_enc_features |= feat; + + dev->needed_headroom = OVPN_HEAD_ROOM; + dev->needed_tailroom = OVPN_MAX_PADDING; + + SET_NETDEV_DEVTYPE(dev, &ovpn_type); +} + static int ovpn_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - return -EOPNOTSUPP; + struct ovpn_struct *ovpn = netdev_priv(dev); + enum ovpn_mode mode = OVPN_MODE_P2P; + + if (data && data[IFLA_OVPN_MODE]) { + mode = nla_get_u8(data[IFLA_OVPN_MODE]); + netdev_dbg(dev, "setting device mode: %u\n", mode); + } + + ovpn->dev = dev; + ovpn->mode = mode; + + /* turn carrier explicitly off after registration, this way state is +* clearly defined +*/ + netif_carrier_off(dev); + + return register_netdevice(dev); } static struct rtnl_link_ops ovpn_link_ops = { .kind = OVPN_FAMILY_NAME, .netns_refund = false, + .priv_size = sizeof(struct ovpn_struct), + .setup = ovpn_setup, + .policy = ovpn_policy, + .maxtype = IFLA_OVPN_MAX, .newlink = ovpn_newlink, .dellink = unregister_netdevice_queue, }; @@ -51,26 +145,37 @@ static int ovpn_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct ovpn_struct *ovpn; if (!ovpn_dev_is_valid(dev)) return NOTIFY_DONE; + ovpn = netdev_priv(dev); + switch (state) {
[PATCH net-next v11 06/23] ovpn: introduce the ovpn_peer object
An ovpn_peer object holds the whole status of a remote peer (regardless whether it is a server or a client). This includes status for crypto, tx/rx buffers, napi, etc. Only support for one peer is introduced (P2P mode). Multi peer support is introduced with a later patch. Along with the ovpn_peer, also the ovpn_bind object is introcued as the two are strictly related. An ovpn_bind object wraps a sockaddr representing the local coordinates being used to talk to a specific peer. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 2 + drivers/net/ovpn/bind.c | 58 +++ drivers/net/ovpn/bind.h | 117 ++ drivers/net/ovpn/main.c | 11 ++ drivers/net/ovpn/main.h | 2 + drivers/net/ovpn/ovpnstruct.h | 4 + drivers/net/ovpn/peer.c | 354 ++ drivers/net/ovpn/peer.h | 79 ++ 8 files changed, 627 insertions(+) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index 201dc001419f1d99ae95c0ee0f96e68f8a4eac16..ce13499b3e1775a7f2a9ce16c6cb0aa088f93685 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -7,7 +7,9 @@ # Author: Antonio Quartulli obj-$(CONFIG_OVPN) := ovpn.o +ovpn-y += bind.o ovpn-y += main.o ovpn-y += io.o ovpn-y += netlink.o ovpn-y += netlink-gen.o +ovpn-y += peer.o diff --git a/drivers/net/ovpn/bind.c b/drivers/net/ovpn/bind.c new file mode 100644 index ..b4d2ccec2ceddf43bc445b489cc62a578ef0ad0a --- /dev/null +++ b/drivers/net/ovpn/bind.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OpenVPN data channel offload + * + * Copyright (C) 2012-2024 OpenVPN, Inc. + * + * Author:James Yonan + * Antonio Quartulli + */ + +#include +#include + +#include "ovpnstruct.h" +#include "bind.h" +#include "peer.h" + +/** + * ovpn_bind_from_sockaddr - retrieve binding matching sockaddr + * @ss: the sockaddr to match + * + * Return: the bind matching the passed sockaddr if found, NULL otherwise + */ +struct ovpn_bind *ovpn_bind_from_sockaddr(const struct sockaddr_storage *ss) +{ + struct ovpn_bind *bind; + size_t sa_len; + + if (ss->ss_family == AF_INET) + sa_len = sizeof(struct sockaddr_in); + else if (ss->ss_family == AF_INET6) + sa_len = sizeof(struct sockaddr_in6); + else + return ERR_PTR(-EAFNOSUPPORT); + + bind = kzalloc(sizeof(*bind), GFP_ATOMIC); + if (unlikely(!bind)) + return ERR_PTR(-ENOMEM); + + memcpy(&bind->remote, ss, sa_len); + + return bind; +} + +/** + * ovpn_bind_reset - assign new binding to peer + * @peer: the peer whose binding has to be replaced + * @new: the new bind to assign + */ +void ovpn_bind_reset(struct ovpn_peer *peer, struct ovpn_bind *new) +{ + struct ovpn_bind *old; + + spin_lock_bh(&peer->lock); + old = rcu_replace_pointer(peer->bind, new, true); + spin_unlock_bh(&peer->lock); + + kfree_rcu(old, rcu); +} diff --git a/drivers/net/ovpn/bind.h b/drivers/net/ovpn/bind.h new file mode 100644 index ..859213d5040deb36c416eafcf5c6ab31c4d52c7a --- /dev/null +++ b/drivers/net/ovpn/bind.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* OpenVPN data channel offload + * + * Copyright (C) 2012-2024 OpenVPN, Inc. + * + * Author:James Yonan + * Antonio Quartulli + */ + +#ifndef _NET_OVPN_OVPNBIND_H_ +#define _NET_OVPN_OVPNBIND_H_ + +#include +#include +#include +#include +#include +#include + +struct ovpn_peer; + +/** + * union ovpn_sockaddr - basic transport layer address + * @in4: IPv4 address + * @in6: IPv6 address + */ +union ovpn_sockaddr { + struct sockaddr_in in4; + struct sockaddr_in6 in6; +}; + +/** + * struct ovpn_bind - remote peer binding + * @remote: the remote peer sockaddress + * @local: local endpoint used to talk to the peer + * @local.ipv4: local IPv4 used to talk to the peer + * @local.ipv6: local IPv6 used to talk to the peer + * @rcu: used to schedule RCU cleanup job + */ +struct ovpn_bind { + union ovpn_sockaddr remote; /* remote sockaddr */ + + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } local; + + struct rcu_head rcu; +}; + +/** + * skb_protocol_to_family - translate skb->protocol to AF_INET or AF_INET6 + * @skb: the packet sk_buff to inspect + * + * Return: AF_INET, AF_INET6 or 0 in case of unknown protocol + */ +static inline unsigned short skb_protocol_to_family(const struct sk_buff *skb) +{ + switch (skb->protocol) { + case htons(ETH_P_IP): + return AF_INET; + case htons(ETH_P_IPV6): + return AF_INET6; + default: + return 0; + } +} + +/** + * ovpn_bind_skb_src_match - match packet
[PATCH net-next v11 08/23] ovpn: implement basic TX path (UDP)
Packets sent over the ovpn interface are processed and transmitted to the connected peer, if any. Implementation is UDP only. TCP will be added by a later patch. Note: no crypto/encapsulation exists yet. packets are just captured and sent. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 138 +++- drivers/net/ovpn/peer.c | 37 +++- drivers/net/ovpn/peer.h | 4 + drivers/net/ovpn/skb.h | 51 +++ drivers/net/ovpn/udp.c | 232 drivers/net/ovpn/udp.h | 8 ++ 6 files changed, 468 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index ad3813419c33cbdfe7e8ad6f5c8b444a3540a69f..77ba4d33ae0bd2f52e8bd1c06a182d24285297b4 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -9,14 +9,150 @@ #include #include +#include #include "io.h" +#include "ovpnstruct.h" +#include "peer.h" +#include "udp.h" +#include "skb.h" +#include "socket.h" + +static void ovpn_encrypt_post(struct sk_buff *skb, int ret) +{ + struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; + + if (unlikely(ret < 0)) + goto err; + + skb_mark_not_on_list(skb); + + switch (peer->sock->sock->sk->sk_protocol) { + case IPPROTO_UDP: + ovpn_udp_send_skb(peer->ovpn, peer, skb); + break; + default: + /* no transport configured yet */ + goto err; + } + /* skb passed down the stack - don't free it */ + skb = NULL; +err: + if (unlikely(skb)) + dev_core_stats_tx_dropped_inc(peer->ovpn->dev); + ovpn_peer_put(peer); + kfree_skb(skb); +} + +static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb) +{ + ovpn_skb_cb(skb)->peer = peer; + + /* take a reference to the peer because the crypto code may run async. +* ovpn_encrypt_post() will release it upon completion +*/ + if (unlikely(!ovpn_peer_hold(peer))) { + DEBUG_NET_WARN_ON_ONCE(1); + return false; + } + + ovpn_encrypt_post(skb, 0); + return true; +} + +/* send skb to connected peer, if any */ +static void ovpn_send(struct ovpn_struct *ovpn, struct sk_buff *skb, + struct ovpn_peer *peer) +{ + struct sk_buff *curr, *next; + + if (likely(!peer)) + /* retrieve peer serving the destination IP of this packet */ + peer = ovpn_peer_get_by_dst(ovpn, skb); + if (unlikely(!peer)) { + net_dbg_ratelimited("%s: no peer to send data to\n", + ovpn->dev->name); + dev_core_stats_tx_dropped_inc(ovpn->dev); + goto drop; + } + + /* this might be a GSO-segmented skb list: process each skb +* independently +*/ + skb_list_walk_safe(skb, curr, next) + if (unlikely(!ovpn_encrypt_one(peer, curr))) { + dev_core_stats_tx_dropped_inc(ovpn->dev); + kfree_skb(curr); + } + + /* skb passed over, no need to free */ + skb = NULL; +drop: + if (likely(peer)) + ovpn_peer_put(peer); + kfree_skb_list(skb); +} /* Send user data to the network */ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) { + struct ovpn_struct *ovpn = netdev_priv(dev); + struct sk_buff *segments, *curr, *next; + struct sk_buff_head skb_list; + __be16 proto; + int ret; + + /* reset netfilter state */ + nf_reset_ct(skb); + + /* verify IP header size in network packet */ + proto = ovpn_ip_check_protocol(skb); + if (unlikely(!proto || skb->protocol != proto)) { + net_err_ratelimited("%s: dropping malformed payload packet\n", + dev->name); + dev_core_stats_tx_dropped_inc(ovpn->dev); + goto drop; + } + + if (skb_is_gso(skb)) { + segments = skb_gso_segment(skb, 0); + if (IS_ERR(segments)) { + ret = PTR_ERR(segments); + net_err_ratelimited("%s: cannot segment packet: %d\n", + dev->name, ret); + dev_core_stats_tx_dropped_inc(ovpn->dev); + goto drop; + } + + consume_skb(skb); + skb = segments; + } + + /* from this moment on, "skb" might be a list */ + + __skb_queue_head_init(&skb_list); + skb_list_walk_safe(skb, curr, next) { + skb_mark_not_on_list(curr); + + curr = skb_share_check(curr, GFP_ATOMIC); +
[PATCH net-next v11 05/23] ovpn: keep carrier always on
An ovpn interface will keep carrier always on and let the user decide when an interface should be considered disconnected. This way, even if an ovpn interface is not connected to any peer, it can still retain all IPs and routes and thus prevent any data leak. Signed-off-by: Antonio Quartulli Reviewed-by: Andrew Lunn --- drivers/net/ovpn/main.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index eead7677b8239eb3c48bb26ca95492d88512b8d4..eaa83a8662e4ac2c758201008268f9633643c0b6 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -31,6 +31,13 @@ static void ovpn_struct_free(struct net_device *net) static int ovpn_net_open(struct net_device *dev) { + /* ovpn keeps the carrier always on to avoid losing IP or route +* configuration upon disconnection. This way it can prevent leaks +* of traffic outside of the VPN tunnel. +* The user may override this behaviour by tearing down the interface +* manually. +*/ + netif_carrier_on(dev); netif_tx_start_all_queues(dev); return 0; } -- 2.45.2
[PATCH net-next v11 03/23] ovpn: add basic netlink support
This commit introduces basic netlink support with family registration/unregistration functionalities and stub pre/post-doit. More importantly it introduces the YAML uAPI description along with its auto-generated files: - include/uapi/linux/ovpn.h - drivers/net/ovpn/netlink-gen.c - drivers/net/ovpn/netlink-gen.h Cc: donald.hun...@gmail.com Signed-off-by: Antonio Quartulli --- Documentation/netlink/specs/ovpn.yaml | 362 ++ MAINTAINERS | 2 + drivers/net/ovpn/Makefile | 2 + drivers/net/ovpn/main.c | 15 +- drivers/net/ovpn/netlink-gen.c| 212 drivers/net/ovpn/netlink-gen.h| 41 drivers/net/ovpn/netlink.c| 157 +++ drivers/net/ovpn/netlink.h| 15 ++ drivers/net/ovpn/ovpnstruct.h | 25 +++ include/uapi/linux/ovpn.h | 109 ++ 10 files changed, 939 insertions(+), 1 deletion(-) diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml new file mode 100644 index ..79339c25d607f1b5d15a0a973f6fc23637e158a2 --- /dev/null +++ b/Documentation/netlink/specs/ovpn.yaml @@ -0,0 +1,362 @@ +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +# +# Author: Antonio Quartulli +# +# Copyright (c) 2024, OpenVPN Inc. +# + +name: ovpn + +protocol: genetlink + +doc: Netlink protocol to control OpenVPN network devices + +definitions: + - +type: const +name: nonce-tail-size +value: 8 + - +type: enum +name: cipher-alg +entries: [ none, aes-gcm, chacha20-poly1305 ] + - +type: enum +name: del-peer-reason +entries: [ teardown, userspace, expired, transport-error, transport-disconnect ] + - +type: enum +name: key-slot +entries: [ primary, secondary ] + +attribute-sets: + - +name: peer +attributes: + - +name: id +type: u32 +doc: | + The unique ID of the peer. To be used to identify peers during + operations +checks: + max: 0xFF + - +name: remote-ipv4 +type: u32 +doc: The remote IPv4 address of the peer +byte-order: big-endian +display-hint: ipv4 + - +name: remote-ipv6 +type: binary +doc: The remote IPv6 address of the peer +display-hint: ipv6 +checks: + exact-len: 16 + - +name: remote-ipv6-scope-id +type: u32 +doc: The scope id of the remote IPv6 address of the peer (RFC2553) + - +name: remote-port +type: u16 +doc: The remote port of the peer +byte-order: big-endian +checks: + min: 1 + - +name: socket +type: u32 +doc: The socket to be used to communicate with the peer + - +name: vpn-ipv4 +type: u32 +doc: The IPv4 address assigned to the peer by the server +byte-order: big-endian +display-hint: ipv4 + - +name: vpn-ipv6 +type: binary +doc: The IPv6 address assigned to the peer by the server +display-hint: ipv6 +checks: + exact-len: 16 + - +name: local-ipv4 +type: u32 +doc: The local IPv4 to be used to send packets to the peer (UDP only) +byte-order: big-endian +display-hint: ipv4 + - +name: local-ipv6 +type: binary +doc: The local IPv6 to be used to send packets to the peer (UDP only) +display-hint: ipv6 +checks: + exact-len: 16 + - +name: local-port +type: u16 +doc: The local port to be used to send packets to the peer (UDP only) +byte-order: big-endian +checks: + min: 1 + - +name: keepalive-interval +type: u32 +doc: | + The number of seconds after which a keep alive message is sent to the + peer + - +name: keepalive-timeout +type: u32 +doc: | + The number of seconds from the last activity after which the peer is + assumed dead + - +name: del-reason +type: u32 +doc: The reason why a peer was deleted +enum: del-peer-reason + - +name: vpn-rx-bytes +type: uint +doc: Number of bytes received over the tunnel + - +name: vpn-tx-bytes +type: uint +doc: Number of bytes transmitted over the tunnel + - +name: vpn-rx-packets +type: uint +doc: Number of packets received over the tunnel + - +name: vpn-tx-packets +type: uint +doc: Number of packets transmitted over the tunnel + - +name: link-rx-bytes +type: uint +doc: Number of bytes received at the transport level + - +name: link-tx-bytes
[PATCH net-next v11 09/23] ovpn: implement basic RX path (UDP)
Packets received over the socket are forwarded to the user device. Implementation is UDP only. TCP will be added by a later patch. Note: no decryption/decapsulation exists yet, packets are forwarded as they arrive without much processing. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 66 ++- drivers/net/ovpn/io.h | 2 + drivers/net/ovpn/main.c | 13 +- drivers/net/ovpn/ovpnstruct.h | 3 ++ drivers/net/ovpn/proto.h | 75 ++ drivers/net/ovpn/socket.c | 24 ++ drivers/net/ovpn/udp.c| 104 +- drivers/net/ovpn/udp.h| 3 +- 8 files changed, 286 insertions(+), 4 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 77ba4d33ae0bd2f52e8bd1c06a182d24285297b4..791a1b117125118b179cb13cdfd5fbab6523a360 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -9,15 +9,79 @@ #include #include +#include #include -#include "io.h" #include "ovpnstruct.h" #include "peer.h" +#include "io.h" +#include "netlink.h" +#include "proto.h" #include "udp.h" #include "skb.h" #include "socket.h" +/* Called after decrypt to write the IP packet to the device. + * This method is expected to manage/free the skb. + */ +static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb) +{ + unsigned int pkt_len; + + /* we can't guarantee the packet wasn't corrupted before entering the +* VPN, therefore we give other layers a chance to check that +*/ + skb->ip_summed = CHECKSUM_NONE; + + /* skb hash for transport packet no longer valid after decapsulation */ + skb_clear_hash(skb); + + /* post-decrypt scrub -- prepare to inject encapsulated packet onto the +* interface, based on __skb_tunnel_rx() in dst.h +*/ + skb->dev = peer->ovpn->dev; + skb_set_queue_mapping(skb, 0); + skb_scrub_packet(skb, true); + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb_probe_transport_header(skb); + skb_reset_inner_headers(skb); + + memset(skb->cb, 0, sizeof(skb->cb)); + + /* cause packet to be "received" by the interface */ + pkt_len = skb->len; + if (likely(gro_cells_receive(&peer->ovpn->gro_cells, +skb) == NET_RX_SUCCESS)) + /* update RX stats with the size of decrypted packet */ + dev_sw_netstats_rx_add(peer->ovpn->dev, pkt_len); +} + +static void ovpn_decrypt_post(struct sk_buff *skb, int ret) +{ + struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; + + if (unlikely(ret < 0)) + goto drop; + + ovpn_netdev_write(peer, skb); + /* skb is passed to upper layer - don't free it */ + skb = NULL; +drop: + if (unlikely(skb)) + dev_core_stats_rx_dropped_inc(peer->ovpn->dev); + ovpn_peer_put(peer); + kfree_skb(skb); +} + +/* pick next packet from RX queue, decrypt and forward it to the device */ +void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb) +{ + ovpn_skb_cb(skb)->peer = peer; + ovpn_decrypt_post(skb, 0); +} + static void ovpn_encrypt_post(struct sk_buff *skb, int ret) { struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; diff --git a/drivers/net/ovpn/io.h b/drivers/net/ovpn/io.h index aa259be66441f7b0262f39da12d6c3dce0a9b24c..9667a0a470e0b4b427524fffb5b9b395007e5a2f 100644 --- a/drivers/net/ovpn/io.h +++ b/drivers/net/ovpn/io.h @@ -12,4 +12,6 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev); +void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb); + #endif /* _NET_OVPN_OVPN_H_ */ diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 5492ce07751d135c1484fe1ed8227c646df94969..73348765a8cf24321aa6be78e75f607d6dbffb1d 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,16 @@ static void ovpn_struct_free(struct net_device *net) static int ovpn_net_init(struct net_device *dev) { - return 0; + struct ovpn_struct *ovpn = netdev_priv(dev); + + return gro_cells_init(&ovpn->gro_cells, dev); +} + +static void ovpn_net_uninit(struct net_device *dev) +{ + struct ovpn_struct *ovpn = netdev_priv(dev); + + gro_cells_destroy(&ovpn->gro_cells); } static int ovpn_net_open(struct net_device *dev) @@ -56,6 +66,7 @@ static int ovpn_net_stop(struct net_device *dev) static const struct net_device_ops ovpn_netdev_ops = { .ndo_init = ovpn_net_init, + .ndo_uninit = ovpn_net_uninit, .ndo_op
[PATCH net-next v11 11/23] ovpn: store tunnel and transport statistics
Byte/packet counters for in-tunnel and transport streams are now initialized and updated as needed. To be exported via netlink. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 1 + drivers/net/ovpn/crypto_aead.c | 2 ++ drivers/net/ovpn/io.c | 11 ++ drivers/net/ovpn/peer.c| 2 ++ drivers/net/ovpn/peer.h| 5 + drivers/net/ovpn/skb.h | 1 + drivers/net/ovpn/stats.c | 21 +++ drivers/net/ovpn/stats.h | 47 ++ 8 files changed, 90 insertions(+) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index ccdaeced1982c851475657860a005ff2b9dfbd13..d43fda72646bdc7644d9a878b56da0a0e5680c98 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -17,4 +17,5 @@ ovpn-y += netlink-gen.o ovpn-y += peer.o ovpn-y += pktid.o ovpn-y += socket.o +ovpn-y += stats.o ovpn-y += udp.o diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c index f9e3feb297b19868b1084048933796fcc7a47d6e..072bb0881764752520e8e26e18337c1274ce1aa4 100644 --- a/drivers/net/ovpn/crypto_aead.c +++ b/drivers/net/ovpn/crypto_aead.c @@ -48,6 +48,7 @@ int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks, int nfrags, ret; u32 pktid, op; + ovpn_skb_cb(skb)->orig_len = skb->len; ovpn_skb_cb(skb)->peer = peer; ovpn_skb_cb(skb)->ks = ks; @@ -159,6 +160,7 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks, payload_offset = OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE + tag_size; payload_len = skb->len - payload_offset; + ovpn_skb_cb(skb)->orig_len = skb->len; ovpn_skb_cb(skb)->payload_offset = payload_offset; ovpn_skb_cb(skb)->peer = peer; ovpn_skb_cb(skb)->ks = ks; diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 4c81c4547d35d2a73f680ef1f5d8853ffbd952e0..d56e74660c7be9020b5bdf7971322d41afd436d6 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "ovpnstruct.h" #include "peer.h" @@ -68,6 +69,7 @@ void ovpn_decrypt_post(void *data, int ret) unsigned int payload_offset = 0; struct sk_buff *skb = data; struct ovpn_peer *peer; + unsigned int orig_len; __be16 proto; __be32 *pid; @@ -80,6 +82,7 @@ void ovpn_decrypt_post(void *data, int ret) payload_offset = ovpn_skb_cb(skb)->payload_offset; ks = ovpn_skb_cb(skb)->ks; peer = ovpn_skb_cb(skb)->peer; + orig_len = ovpn_skb_cb(skb)->orig_len; /* crypto is done, cleanup skb CB and its members */ @@ -136,6 +139,10 @@ void ovpn_decrypt_post(void *data, int ret) goto drop; } + /* increment RX stats */ + ovpn_peer_stats_increment_rx(&peer->vpn_stats, skb->len); + ovpn_peer_stats_increment_rx(&peer->link_stats, orig_len); + ovpn_netdev_write(peer, skb); /* skb is passed to upper layer - don't free it */ skb = NULL; @@ -175,6 +182,7 @@ void ovpn_encrypt_post(void *data, int ret) struct ovpn_crypto_key_slot *ks; struct sk_buff *skb = data; struct ovpn_peer *peer; + unsigned int orig_len; /* encryption is happening asynchronously. This function will be * called later by the crypto callback with a proper return value @@ -184,6 +192,7 @@ void ovpn_encrypt_post(void *data, int ret) ks = ovpn_skb_cb(skb)->ks; peer = ovpn_skb_cb(skb)->peer; + orig_len = ovpn_skb_cb(skb)->orig_len; /* crypto is done, cleanup skb CB and its members */ @@ -197,6 +206,8 @@ void ovpn_encrypt_post(void *data, int ret) goto err; skb_mark_not_on_list(skb); + ovpn_peer_stats_increment_tx(&peer->link_stats, skb->len); + ovpn_peer_stats_increment_tx(&peer->vpn_stats, orig_len); switch (peer->sock->sock->sk->sk_protocol) { case IPPROTO_UDP: diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 98ae7662f1e76811e625dc5f4b4c5c884856fbd6..5025bfb759d6a5f31e3f2ec094fe561fbdb9f451 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -48,6 +48,8 @@ struct ovpn_peer *ovpn_peer_new(struct ovpn_struct *ovpn, u32 id) ovpn_crypto_state_init(&peer->crypto); spin_lock_init(&peer->lock); kref_init(&peer->refcount); + ovpn_peer_stats_init(&peer->vpn_stats); + ovpn_peer_stats_init(&peer->link_stats); ret = dst_cache_init(&peer->dst_cache, GFP_KERNEL); if (ret < 0) { diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index 754fea470d1b4787f64a931d6c6adc24182fc16f..eb1e31e854fb
[PATCH net-next v11 22/23] ovpn: add basic ethtool support
Implement support for basic ethtool functionality. Note that ovpn is a virtual device driver, therefore various ethtool APIs are just not meaningful and thus not implemented. Signed-off-by: Antonio Quartulli Reviewed-by: Andrew Lunn --- drivers/net/ovpn/main.c | 15 +++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 1bd563e3f16f49dd01c897fbe79cbd90f4b8e9aa..9dcf51ae1497dda17d418b762011b04bfd0521df 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -7,6 +7,7 @@ * James Yonan */ +#include #include #include #include @@ -96,6 +97,19 @@ bool ovpn_dev_is_valid(const struct net_device *dev) return dev->netdev_ops->ndo_start_xmit == ovpn_net_xmit; } +static void ovpn_get_drvinfo(struct net_device *dev, +struct ethtool_drvinfo *info) +{ + strscpy(info->driver, OVPN_FAMILY_NAME, sizeof(info->driver)); + strscpy(info->bus_info, "ovpn", sizeof(info->bus_info)); +} + +static const struct ethtool_ops ovpn_ethtool_ops = { + .get_drvinfo= ovpn_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ts_info= ethtool_op_get_ts_info, +}; + static void ovpn_setup(struct net_device *dev) { /* compute the overhead considering AEAD encryption */ @@ -111,6 +125,7 @@ static void ovpn_setup(struct net_device *dev) dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + dev->ethtool_ops = &ovpn_ethtool_ops; dev->netdev_ops = &ovpn_netdev_ops; dev->priv_destructor = ovpn_struct_free; -- 2.45.2
[PATCH net-next v11 18/23] ovpn: implement peer add/get/dump/delete via netlink
This change introduces the netlink command needed to add, delete and retrieve/dump known peers. Userspace is expected to use these commands to handle known peer lifecycles. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 578 - drivers/net/ovpn/peer.c| 48 ++-- drivers/net/ovpn/peer.h| 5 + 3 files changed, 609 insertions(+), 22 deletions(-) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 2cc34eb1d1d870c6705714cb971c3c5dfb04afda..d504445325ef82db04f87367c858adaf025f6297 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -16,6 +17,10 @@ #include "io.h" #include "netlink.h" #include "netlink-gen.h" +#include "bind.h" +#include "packet.h" +#include "peer.h" +#include "socket.h" MODULE_ALIAS_GENL_FAMILY(OVPN_FAMILY_NAME); @@ -86,29 +91,592 @@ void ovpn_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb, netdev_put(ovpn->dev, &ovpn->dev_tracker); } +static int ovpn_nl_attr_sockaddr_remote(struct nlattr **attrs, + struct sockaddr_storage *ss) +{ + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; + struct in6_addr *in6; + __be16 port = 0; + __be32 *in; + int af; + + ss->ss_family = AF_UNSPEC; + + if (attrs[OVPN_A_PEER_REMOTE_PORT]) + port = nla_get_be16(attrs[OVPN_A_PEER_REMOTE_PORT]); + + if (attrs[OVPN_A_PEER_REMOTE_IPV4]) { + af = AF_INET; + ss->ss_family = AF_INET; + in = nla_data(attrs[OVPN_A_PEER_REMOTE_IPV4]); + } else if (attrs[OVPN_A_PEER_REMOTE_IPV6]) { + af = AF_INET6; + ss->ss_family = AF_INET6; + in6 = nla_data(attrs[OVPN_A_PEER_REMOTE_IPV6]); + } else { + return AF_UNSPEC; + } + + switch (ss->ss_family) { + case AF_INET6: + /* If this is a regular IPv6 just break and move on, +* otherwise switch to AF_INET and extract the IPv4 accordingly +*/ + if (!ipv6_addr_v4mapped(in6)) { + sin6 = (struct sockaddr_in6 *)ss; + sin6->sin6_port = port; + memcpy(&sin6->sin6_addr, in6, sizeof(*in6)); + break; + } + + /* v4-mapped-v6 address */ + ss->ss_family = AF_INET; + in = &in6->s6_addr32[3]; + fallthrough; + case AF_INET: + sin = (struct sockaddr_in *)ss; + sin->sin_port = port; + sin->sin_addr.s_addr = *in; + break; + } + + /* don't return ss->ss_family as it may have changed in case of +* v4-mapped-v6 address +*/ + return af; +} + +static u8 *ovpn_nl_attr_local_ip(struct nlattr **attrs) +{ + u8 *addr6; + + if (!attrs[OVPN_A_PEER_LOCAL_IPV4] && !attrs[OVPN_A_PEER_LOCAL_IPV6]) + return NULL; + + if (attrs[OVPN_A_PEER_LOCAL_IPV4]) + return nla_data(attrs[OVPN_A_PEER_LOCAL_IPV4]); + + addr6 = nla_data(attrs[OVPN_A_PEER_LOCAL_IPV6]); + /* this is an IPv4-mapped IPv6 address, therefore extract the actual +* v4 address from the last 4 bytes +*/ + if (ipv6_addr_v4mapped((struct in6_addr *)addr6)) + return addr6 + 12; + + return addr6; +} + +static int ovpn_nl_peer_precheck(struct ovpn_struct *ovpn, +struct genl_info *info, +struct nlattr **attrs) +{ + if (NL_REQ_ATTR_CHECK(info->extack, info->attrs[OVPN_A_PEER], attrs, + OVPN_A_PEER_ID)) + return -EINVAL; + + if (attrs[OVPN_A_PEER_REMOTE_IPV4] && attrs[OVPN_A_PEER_REMOTE_IPV6]) { + NL_SET_ERR_MSG_MOD(info->extack, + "cannot specify both remote IPv4 or IPv6 address"); + return -EINVAL; + } + + if (!attrs[OVPN_A_PEER_REMOTE_IPV4] && + !attrs[OVPN_A_PEER_REMOTE_IPV6] && attrs[OVPN_A_PEER_REMOTE_PORT]) { + NL_SET_ERR_MSG_MOD(info->extack, + "cannot specify remote port without IP address"); + return -EINVAL; + } + + if (!attrs[OVPN_A_PEER_REMOTE_IPV4] && + attrs[OVPN_A_PEER_LOCAL_IPV4]) { + NL_SET_ERR_MSG_MOD(info->extack, + "cannot specify local IPv4 address without remote"); + return -EINVAL; + } + + if (!at
[PATCH net-next v11 19/23] ovpn: implement key add/get/del/swap via netlink
This change introduces the netlink commands needed to add, get, delete and swap keys for a specific peer. Userspace is expected to use these commands to create, inspect (non sensible data only), destroy and rotate session keys for a specific peer. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/crypto.c | 42 ++ drivers/net/ovpn/crypto.h | 4 + drivers/net/ovpn/crypto_aead.c | 17 +++ drivers/net/ovpn/crypto_aead.h | 2 + drivers/net/ovpn/netlink.c | 308 - 5 files changed, 369 insertions(+), 4 deletions(-) diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c index f1f7510e2f735e367f96eb4982ba82c9af3c8bfc..cfb014c947b968752ba3dab84ec42dc8ec086379 100644 --- a/drivers/net/ovpn/crypto.c +++ b/drivers/net/ovpn/crypto.c @@ -151,3 +151,45 @@ void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs) spin_unlock_bh(&cs->lock); } + +/** + * ovpn_crypto_config_get - populate keyconf object with non-sensible key data + * @cs: the crypto state to extract the key data from + * @slot: the specific slot to inspect + * @keyconf: the output object to populate + * + * Return: 0 on success or a negative error code otherwise + */ +int ovpn_crypto_config_get(struct ovpn_crypto_state *cs, + enum ovpn_key_slot slot, + struct ovpn_key_config *keyconf) +{ + struct ovpn_crypto_key_slot *ks; + int idx; + + switch (slot) { + case OVPN_KEY_SLOT_PRIMARY: + idx = cs->primary_idx; + break; + case OVPN_KEY_SLOT_SECONDARY: + idx = !cs->primary_idx; + break; + default: + return -EINVAL; + } + + rcu_read_lock(); + ks = rcu_dereference(cs->slots[idx]); + if (!ks || (ks && !ovpn_crypto_key_slot_hold(ks))) { + rcu_read_unlock(); + return -ENOENT; + } + rcu_read_unlock(); + + keyconf->cipher_alg = ovpn_aead_crypto_alg(ks); + keyconf->key_id = ks->key_id; + + ovpn_crypto_key_slot_put(ks); + + return 0; +} diff --git a/drivers/net/ovpn/crypto.h b/drivers/net/ovpn/crypto.h index 3b437d26b531c3034cca5343c755ef9c7ef57276..96fd41f4b81b74f8a3ecfe33ee24ba0122d222fe 100644 --- a/drivers/net/ovpn/crypto.h +++ b/drivers/net/ovpn/crypto.h @@ -136,4 +136,8 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs); void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs); +int ovpn_crypto_config_get(struct ovpn_crypto_state *cs, + enum ovpn_key_slot slot, + struct ovpn_key_config *keyconf); + #endif /* _NET_OVPN_OVPNCRYPTO_H_ */ diff --git a/drivers/net/ovpn/crypto_aead.c b/drivers/net/ovpn/crypto_aead.c index 072bb0881764752520e8e26e18337c1274ce1aa4..25e4e4a453b2bc499aec9a192fe3d86ba1aac511 100644 --- a/drivers/net/ovpn/crypto_aead.c +++ b/drivers/net/ovpn/crypto_aead.c @@ -367,3 +367,20 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc) ovpn_aead_crypto_key_slot_destroy(ks); return ERR_PTR(ret); } + +enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks) +{ + const char *alg_name; + + if (!ks->encrypt) + return OVPN_CIPHER_ALG_NONE; + + alg_name = crypto_tfm_alg_name(crypto_aead_tfm(ks->encrypt)); + + if (!strcmp(alg_name, ALG_NAME_AES)) + return OVPN_CIPHER_ALG_AES_GCM; + else if (!strcmp(alg_name, ALG_NAME_CHACHAPOLY)) + return OVPN_CIPHER_ALG_CHACHA20_POLY1305; + else + return OVPN_CIPHER_ALG_NONE; +} diff --git a/drivers/net/ovpn/crypto_aead.h b/drivers/net/ovpn/crypto_aead.h index 77ee8141599bc06b0dc664c5b0a4dae660a89238..fb65be82436edd7ff89b171f7a89c9103b617d1f 100644 --- a/drivers/net/ovpn/crypto_aead.h +++ b/drivers/net/ovpn/crypto_aead.h @@ -28,4 +28,6 @@ struct ovpn_crypto_key_slot * ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc); void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks); +enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks); + #endif /* _NET_OVPN_OVPNAEAD_H_ */ diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index d504445325ef82db04f87367c858adaf025f6297..fe9377b9b8145784917460cd5f222bc7fae4d8db 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -18,6 +18,7 @@ #include "netlink.h" #include "netlink-gen.h" #include "bind.h" +#include "crypto.h" #include "packet.h" #include "peer.h" #include "socket.h" @@ -679,24 +680,323 @@ int ovpn_nl_peer_del_doit(struct sk_buff *skb, struct genl_info *info) return ret; } +static int ovpn_nl_get_key_dir(struct genl_info *info, struct nlattr *key, + enum ovpn_ciph
[PATCH net-next v11 21/23] ovpn: notify userspace when a peer is deleted
Whenever a peer is deleted, send a notification to userspace so that it can react accordingly. This is most important when a peer is deleted due to ping timeout, because it all happens in kernelspace and thus userspace has no direct way to learn about it. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 55 ++ drivers/net/ovpn/netlink.h | 1 + drivers/net/ovpn/peer.c| 1 + 3 files changed, 57 insertions(+) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 2b2ba1a810a0e87fb9ffb43b988fa52725a9589b..4d7d835cb47fd1f03d7cdafa2eda9f03065b8024 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -999,6 +999,61 @@ int ovpn_nl_key_del_doit(struct sk_buff *skb, struct genl_info *info) return 0; } +/** + * ovpn_nl_peer_del_notify - notify userspace about peer being deleted + * @peer: the peer being deleted + * + * Return: 0 on success or a negative error code otherwise + */ +int ovpn_nl_peer_del_notify(struct ovpn_peer *peer) +{ + struct sk_buff *msg; + struct nlattr *attr; + int ret = -EMSGSIZE; + void *hdr; + + netdev_info(peer->ovpn->dev, "deleting peer with id %u, reason %d\n", + peer->id, peer->delete_reason); + + msg = nlmsg_new(100, GFP_ATOMIC); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_put(msg, 0, 0, &ovpn_nl_family, 0, OVPN_CMD_PEER_DEL_NTF); + if (!hdr) { + ret = -ENOBUFS; + goto err_free_msg; + } + + if (nla_put_u32(msg, OVPN_A_IFINDEX, peer->ovpn->dev->ifindex)) + goto err_cancel_msg; + + attr = nla_nest_start(msg, OVPN_A_PEER); + if (!attr) + goto err_cancel_msg; + + if (nla_put_u8(msg, OVPN_A_PEER_DEL_REASON, peer->delete_reason)) + goto err_cancel_msg; + + if (nla_put_u32(msg, OVPN_A_PEER_ID, peer->id)) + goto err_cancel_msg; + + nla_nest_end(msg, attr); + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(&ovpn_nl_family, dev_net(peer->ovpn->dev), msg, + 0, OVPN_NLGRP_PEERS, GFP_ATOMIC); + + return 0; + +err_cancel_msg: + genlmsg_cancel(msg, hdr); +err_free_msg: + nlmsg_free(msg); + return ret; +} + /** * ovpn_nl_key_swap_notify - notify userspace peer's key must be renewed * @peer: the peer whose key needs to be renewed diff --git a/drivers/net/ovpn/netlink.h b/drivers/net/ovpn/netlink.h index 33390b13c8904d40b629662005a9eb92ff617c3b..4ab3abcf23dba11f6b92e3d69e700693adbc671b 100644 --- a/drivers/net/ovpn/netlink.h +++ b/drivers/net/ovpn/netlink.h @@ -12,6 +12,7 @@ int ovpn_nl_register(void); void ovpn_nl_unregister(void); +int ovpn_nl_peer_del_notify(struct ovpn_peer *peer); int ovpn_nl_key_swap_notify(struct ovpn_peer *peer, u8 key_id); #endif /* _NET_OVPN_NETLINK_H_ */ diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 8cfe1997ec116ae4fe74cd7105d228569e2a66a9..91c608f1ffa1d9dd1535ba308b6adc933dbbf1f1 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -242,6 +242,7 @@ void ovpn_peer_release_kref(struct kref *kref) { struct ovpn_peer *peer = container_of(kref, struct ovpn_peer, refcount); + ovpn_nl_peer_del_notify(peer); ovpn_peer_release(peer); } -- 2.45.2
[PATCH net-next v11 16/23] ovpn: add support for updating local UDP endpoint
In case of UDP links, the local endpoint used to communicate with a given peer may change without a connection restart. Add support for learning the new address in case of change. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 45 + drivers/net/ovpn/peer.h | 3 +++ 2 files changed, 48 insertions(+) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index e8a42212af391916b5321e729f7e8a864d0a541f..3f67d200e283213fcb732d10f9edeb53e0a0e9ee 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -416,6 +416,51 @@ struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_struct *ovpn, u32 peer_id) return peer; } +/** + * ovpn_peer_update_local_endpoint - update local endpoint for peer + * @peer: peer to update the endpoint for + * @skb: incoming packet to retrieve the destination address (local) from + */ +void ovpn_peer_update_local_endpoint(struct ovpn_peer *peer, +struct sk_buff *skb) +{ + struct ovpn_bind *bind; + + rcu_read_lock(); + bind = rcu_dereference(peer->bind); + if (unlikely(!bind)) + goto unlock; + + spin_lock_bh(&peer->lock); + switch (skb_protocol_to_family(skb)) { + case AF_INET: + if (unlikely(bind->local.ipv4.s_addr != ip_hdr(skb)->daddr)) { + netdev_dbg(peer->ovpn->dev, + "%s: learning local IPv4 for peer %d (%pI4 -> %pI4)\n", + __func__, peer->id, &bind->local.ipv4.s_addr, + &ip_hdr(skb)->daddr); + bind->local.ipv4.s_addr = ip_hdr(skb)->daddr; + } + break; + case AF_INET6: + if (unlikely(!ipv6_addr_equal(&bind->local.ipv6, + &ipv6_hdr(skb)->daddr))) { + netdev_dbg(peer->ovpn->dev, + "%s: learning local IPv6 for peer %d (%pI6c -> %pI6c\n", + __func__, peer->id, &bind->local.ipv6, + &ipv6_hdr(skb)->daddr); + bind->local.ipv6 = ipv6_hdr(skb)->daddr; + } + break; + default: + break; + } + spin_unlock_bh(&peer->lock); + +unlock: + rcu_read_unlock(); +} + /** * ovpn_peer_get_by_dst - Lookup peer to send skb to * @ovpn: the private data representing the current VPN session diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index 952927ae78a3ab753aaf2c6cc6f77121bdac34be..1a8638d266b11a4a80ee2f088394d47a7798c3af 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -152,4 +152,7 @@ bool ovpn_peer_check_by_src(struct ovpn_struct *ovpn, struct sk_buff *skb, void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout); void ovpn_peer_keepalive_work(struct work_struct *work); +void ovpn_peer_update_local_endpoint(struct ovpn_peer *peer, +struct sk_buff *skb); + #endif /* _NET_OVPN_OVPNPEER_H_ */ -- 2.45.2
[PATCH net-next v11 15/23] ovpn: implement keepalive mechanism
OpenVPN supports configuring a periodic keepalive packet. message to allow the remote endpoint detect link failures. This change implements the keepalive sending and timer expiring logic. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/io.c | 77 + drivers/net/ovpn/io.h | 5 ++ drivers/net/ovpn/main.c | 3 + drivers/net/ovpn/ovpnstruct.h | 2 + drivers/net/ovpn/peer.c | 188 ++ drivers/net/ovpn/peer.h | 15 drivers/net/ovpn/proto.h | 2 - 7 files changed, 290 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index deda19ab87391f86964ba43088b7847d22420eee..63c140138bf98e5d1df79a2565b666d86513323d 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -27,6 +27,33 @@ #include "skb.h" #include "socket.h" +const unsigned char ovpn_keepalive_message[OVPN_KEEPALIVE_SIZE] = { + 0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb, + 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48 +}; + +/** + * ovpn_is_keepalive - check if skb contains a keepalive message + * @skb: packet to check + * + * Assumes that the first byte of skb->data is defined. + * + * Return: true if skb contains a keepalive or false otherwise + */ +static bool ovpn_is_keepalive(struct sk_buff *skb) +{ + if (*skb->data != ovpn_keepalive_message[0]) + return false; + + if (skb->len != OVPN_KEEPALIVE_SIZE) + return false; + + if (!pskb_may_pull(skb, OVPN_KEEPALIVE_SIZE)) + return false; + + return !memcmp(skb->data, ovpn_keepalive_message, OVPN_KEEPALIVE_SIZE); +} + /* Called after decrypt to write the IP packet to the device. * This method is expected to manage/free the skb. */ @@ -105,6 +132,9 @@ void ovpn_decrypt_post(void *data, int ret) goto drop; } + /* keep track of last received authenticated packet for keepalive */ + peer->last_recv = ktime_get_real_seconds(); + /* point to encapsulated IP packet */ __skb_pull(skb, payload_offset); @@ -121,6 +151,12 @@ void ovpn_decrypt_post(void *data, int ret) goto drop; } + if (ovpn_is_keepalive(skb)) { + net_dbg_ratelimited("%s: ping received from peer %u\n", + peer->ovpn->dev->name, peer->id); + goto drop; + } + net_info_ratelimited("%s: unsupported protocol received from peer %u\n", peer->ovpn->dev->name, peer->id); goto drop; @@ -221,6 +257,10 @@ void ovpn_encrypt_post(void *data, int ret) /* no transport configured yet */ goto err; } + + /* keep track of last sent packet for keepalive */ + peer->last_sent = ktime_get_real_seconds(); + /* skb passed down the stack - don't free it */ skb = NULL; err: @@ -361,3 +401,40 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) kfree_skb_list(skb); return NET_XMIT_DROP; } + +/** + * ovpn_xmit_special - encrypt and transmit an out-of-band message to peer + * @peer: peer to send the message to + * @data: message content + * @len: message length + * + * Assumes that caller holds a reference to peer + */ +void ovpn_xmit_special(struct ovpn_peer *peer, const void *data, + const unsigned int len) +{ + struct ovpn_struct *ovpn; + struct sk_buff *skb; + + ovpn = peer->ovpn; + if (unlikely(!ovpn)) + return; + + skb = alloc_skb(256 + len, GFP_ATOMIC); + if (unlikely(!skb)) + return; + + skb_reserve(skb, 128); + skb->priority = TC_PRIO_BESTEFFORT; + __skb_put_data(skb, data, len); + + /* increase reference counter when passing peer to sending queue */ + if (!ovpn_peer_hold(peer)) { + netdev_dbg(ovpn->dev, "%s: cannot hold peer reference for sending special packet\n", + __func__); + kfree_skb(skb); + return; + } + + ovpn_send(ovpn, skb, peer); +} diff --git a/drivers/net/ovpn/io.h b/drivers/net/ovpn/io.h index ad81dd86924689309b3299573575a1705eddaf99..eb224114152c29f42aadf026212e8d278006b490 100644 --- a/drivers/net/ovpn/io.h +++ b/drivers/net/ovpn/io.h @@ -10,9 +10,14 @@ #ifndef _NET_OVPN_OVPN_H_ #define _NET_OVPN_OVPN_H_ +#define OVPN_KEEPALIVE_SIZE 16 +extern const unsigned char ovpn_keepalive_message[OVPN_KEEPALIVE_SIZE]; + netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev); void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb); +void ovpn_xmit_special(struct ovpn_peer *peer, const void *data, +
[PATCH net-next v11 00/23] Introducing OpenVPN Data Channel Offload
Notable changes from v10: * extended commit message of 23/23 with brief description of the output * Link to v10: https://lore.kernel.org/r/20241025-b4-ovpn-v10-0-b87530777...@openvpn.net Please note that some patches were already reviewed by Andre Lunn, Donald Hunter and Shuah Khan. They have retained the Reviewed-by tag since no major code modification has happened since the review. The latest code can also be found at: https://github.com/OpenVPN/linux-kernel-ovpn Thanks a lot! Best Regards, Antonio Quartulli OpenVPN Inc. --- Antonio Quartulli (23): netlink: add NLA_POLICY_MAX_LEN macro net: introduce OpenVPN Data Channel Offload (ovpn) ovpn: add basic netlink support ovpn: add basic interface creation/destruction/management routines ovpn: keep carrier always on ovpn: introduce the ovpn_peer object ovpn: introduce the ovpn_socket object ovpn: implement basic TX path (UDP) ovpn: implement basic RX path (UDP) ovpn: implement packet processing ovpn: store tunnel and transport statistics ovpn: implement TCP transport ovpn: implement multi-peer support ovpn: implement peer lookup logic ovpn: implement keepalive mechanism ovpn: add support for updating local UDP endpoint ovpn: add support for peer floating ovpn: implement peer add/get/dump/delete via netlink ovpn: implement key add/get/del/swap via netlink ovpn: kill key and notify userspace in case of IV exhaustion ovpn: notify userspace when a peer is deleted ovpn: add basic ethtool support testing/selftests: add test tool and scripts for ovpn module Documentation/netlink/specs/ovpn.yaml | 362 +++ MAINTAINERS| 11 + drivers/net/Kconfig| 14 + drivers/net/Makefile |1 + drivers/net/ovpn/Makefile | 22 + drivers/net/ovpn/bind.c| 54 + drivers/net/ovpn/bind.h| 117 + drivers/net/ovpn/crypto.c | 214 ++ drivers/net/ovpn/crypto.h | 145 ++ drivers/net/ovpn/crypto_aead.c | 386 drivers/net/ovpn/crypto_aead.h | 33 + drivers/net/ovpn/io.c | 462 drivers/net/ovpn/io.h | 25 + drivers/net/ovpn/main.c| 337 +++ drivers/net/ovpn/main.h| 24 + drivers/net/ovpn/netlink-gen.c | 212 ++ drivers/net/ovpn/netlink-gen.h | 41 + drivers/net/ovpn/netlink.c | 1135 ++ drivers/net/ovpn/netlink.h | 18 + drivers/net/ovpn/ovpnstruct.h | 61 + drivers/net/ovpn/packet.h | 40 + drivers/net/ovpn/peer.c| 1201 ++ drivers/net/ovpn/peer.h| 165 ++ drivers/net/ovpn/pktid.c | 130 ++ drivers/net/ovpn/pktid.h | 87 + drivers/net/ovpn/proto.h | 104 + drivers/net/ovpn/skb.h | 56 + drivers/net/ovpn/socket.c | 178 ++ drivers/net/ovpn/socket.h | 55 + drivers/net/ovpn/stats.c | 21 + drivers/net/ovpn/stats.h | 47 + drivers/net/ovpn/tcp.c | 506 + drivers/net/ovpn/tcp.h | 44 + drivers/net/ovpn/udp.c | 406 drivers/net/ovpn/udp.h | 26 + include/net/netlink.h |1 + include/uapi/linux/if_link.h | 15 + include/uapi/linux/ovpn.h | 109 + include/uapi/linux/udp.h |1 + tools/net/ynl/ynl-gen-c.py |4 +- tools/testing/selftests/Makefile |1 + tools/testing/selftests/net/ovpn/.gitignore|2 + tools/testing/selftests/net/ovpn/Makefile | 17 + tools/testing/selftests/net/ovpn/config| 10 + tools/testing/selftests/net/ovpn/data64.key|5 + tools/testing/selftests/net/ovpn/ovpn-cli.c| 2370 tools/testing/selftests/net/ovpn/tcp_peers.txt |5 + .../testing/selftests/net/ovpn/test-chachapoly.sh |9 + tools/testing/selftests/net/ovpn/test-float.sh |9 + tools/testing/selftests/net/ovpn/test-tcp.sh |9 + tools/testing/selftests/net/ovpn/test.sh | 183 ++ tools/testing/selftests/net/ovpn/udp_peers.txt |5 + 52 files changed, 9494 insertions(+), 1 deletion(-) --- base-commit
[PATCH net-next v11 02/23] net: introduce OpenVPN Data Channel Offload (ovpn)
OpenVPN is a userspace software existing since around 2005 that allows users to create secure tunnels. So far OpenVPN has implemented all operations in userspace, which implies several back and forth between kernel and user land in order to process packets (encapsulate/decapsulate, encrypt/decrypt, rerouting..). With `ovpn` we intend to move the fast path (data channel) entirely in kernel space and thus improve user measured throughput over the tunnel. `ovpn` is implemented as a simple virtual network device driver, that can be manipulated by means of the standard RTNL APIs. A device of kind `ovpn` allows only IPv4/6 traffic and can be of type: * P2P (peer-to-peer): any packet sent over the interface will be encapsulated and transmitted to the other side (typical OpenVPN client or peer-to-peer behaviour); * P2MP (point-to-multipoint): packets sent over the interface are transmitted to peers based on existing routes (typical OpenVPN server behaviour). After the interface has been created, OpenVPN in userspace can configure it using a new Netlink API. Specifically it is possible to manage peers and their keys. The OpenVPN control channel is multiplexed over the same transport socket by means of OP codes. Anything that is not DATA_V2 (OpenVPN OP code for data traffic) is sent to userspace and handled there. This way the `ovpn` codebase is kept as compact as possible while focusing on handling data traffic only (fast path). Any OpenVPN control feature (like cipher negotiation, TLS handshake, rekeying, etc.) is still fully handled by the userspace process. When userspace establishes a new connection with a peer, it first performs the handshake and then passes the socket to the `ovpn` kernel module, which takes ownership. From this moment on `ovpn` will handle data traffic for the new peer. When control packets are received on the link, they are forwarded to userspace through the same transport socket they were received on, as userspace is still listening to them. Some events (like peer deletion) are sent to a Netlink multicast group. Although it wasn't easy to convince the community, `ovpn` implements only a limited number of the data-channel features supported by the userspace program. Each feature that made it to `ovpn` was attentively vetted to avoid carrying too much legacy along with us (and to give a clear cut to old and probalby-not-so-useful features). Notably, only encryption using AEAD ciphers (specifically ChaCha20Poly1305 and AES-GCM) was implemented. Supporting any other cipher out there was not deemed useful. Both UDP and TCP sockets ae supported. As explained above, in case of P2MP mode, OpenVPN will use the main system routing table to decide which packet goes to which peer. This implies that no routing table was re-implemented in the `ovpn` kernel module. This kernel module can be enabled by selecting the CONFIG_OVPN entry in the networking drivers section. NOTE: this first patch introduces the very basic framework only. Features are then added patch by patch, however, although each patch will compile and possibly not break at runtime, only after having applied the full set it is expected to see the ovpn module fully working. Cc: steffen.klass...@secunet.com Cc: antony.ant...@secunet.com Signed-off-by: Antonio Quartulli --- MAINTAINERS | 8 drivers/net/Kconfig | 13 ++ drivers/net/Makefile | 1 + drivers/net/ovpn/Makefile | 11 + drivers/net/ovpn/io.c | 22 + drivers/net/ovpn/io.h | 15 ++ drivers/net/ovpn/main.c | 116 ++ drivers/net/ovpn/main.h | 15 ++ include/uapi/linux/udp.h | 1 + 9 files changed, 202 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f39ab140710f16b1245924bfe381cd64d499ff8a..09e193bbc218d74846cbae26f80ada3e04c3692a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17286,6 +17286,14 @@ F: arch/openrisc/ F: drivers/irqchip/irq-ompic.c F: drivers/irqchip/irq-or1k-* +OPENVPN DATA CHANNEL OFFLOAD +M: Antonio Quartulli +L: openvpn-de...@lists.sourceforge.net (moderated for non-subscribers) +L: net...@vger.kernel.org +S: Supported +T: git https://github.com/OpenVPN/linux-kernel-ovpn.git +F: drivers/net/ovpn/ + OPENVSWITCH M: Pravin B Shelar L: net...@vger.kernel.org diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 1fd5acdc73c6af0e1a861867039c3624fc618e25..269b73fcfd348a48174fb96b8f8d4f8788636fa8 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -115,6 +115,19 @@ config WIREGUARD_DEBUG Say N here unless you know what you're doing. +config OVPN + tristate "OpenVPN data channel offload" + depends on NET && INET + select NET_UDP_TUNNEL + select DST_CACHE + select CRYPTO + select CRYPTO_AES + select CRYPTO_GCM + select CRYPTO_CHACHA20POLY1305 + help +
[PATCH net-next v11 10/23] ovpn: implement packet processing
This change implements encryption/decryption and encapsulation/decapsulation of OpenVPN packets. Support for generic crypto state is added along with a wrapper for the AEAD crypto kernel API. Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/Makefile | 3 + drivers/net/ovpn/crypto.c | 153 + drivers/net/ovpn/crypto.h | 139 drivers/net/ovpn/crypto_aead.c | 367 + drivers/net/ovpn/crypto_aead.h | 31 drivers/net/ovpn/io.c | 146 ++-- drivers/net/ovpn/io.h | 3 + drivers/net/ovpn/packet.h | 2 +- drivers/net/ovpn/peer.c| 29 drivers/net/ovpn/peer.h| 6 + drivers/net/ovpn/pktid.c | 130 +++ drivers/net/ovpn/pktid.h | 87 ++ drivers/net/ovpn/proto.h | 31 drivers/net/ovpn/skb.h | 4 + 14 files changed, 1120 insertions(+), 11 deletions(-) diff --git a/drivers/net/ovpn/Makefile b/drivers/net/ovpn/Makefile index 56bddc9bef83e0befde6af3c3565bb91731d7b22..ccdaeced1982c851475657860a005ff2b9dfbd13 100644 --- a/drivers/net/ovpn/Makefile +++ b/drivers/net/ovpn/Makefile @@ -8,10 +8,13 @@ obj-$(CONFIG_OVPN) := ovpn.o ovpn-y += bind.o +ovpn-y += crypto.o +ovpn-y += crypto_aead.o ovpn-y += main.o ovpn-y += io.o ovpn-y += netlink.o ovpn-y += netlink-gen.o ovpn-y += peer.o +ovpn-y += pktid.o ovpn-y += socket.o ovpn-y += udp.o diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c new file mode 100644 index ..f1f7510e2f735e367f96eb4982ba82c9af3c8bfc --- /dev/null +++ b/drivers/net/ovpn/crypto.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* OpenVPN data channel offload + * + * Copyright (C) 2020-2024 OpenVPN, Inc. + * + * Author:James Yonan + * Antonio Quartulli + */ + +#include +#include +#include +#include + +#include "ovpnstruct.h" +#include "main.h" +#include "packet.h" +#include "pktid.h" +#include "crypto_aead.h" +#include "crypto.h" + +static void ovpn_ks_destroy_rcu(struct rcu_head *head) +{ + struct ovpn_crypto_key_slot *ks; + + ks = container_of(head, struct ovpn_crypto_key_slot, rcu); + ovpn_aead_crypto_key_slot_destroy(ks); +} + +void ovpn_crypto_key_slot_release(struct kref *kref) +{ + struct ovpn_crypto_key_slot *ks; + + ks = container_of(kref, struct ovpn_crypto_key_slot, refcount); + call_rcu(&ks->rcu, ovpn_ks_destroy_rcu); +} + +/* can only be invoked when all peer references have been dropped (i.e. RCU + * release routine) + */ +void ovpn_crypto_state_release(struct ovpn_crypto_state *cs) +{ + struct ovpn_crypto_key_slot *ks; + + ks = rcu_access_pointer(cs->slots[0]); + if (ks) { + RCU_INIT_POINTER(cs->slots[0], NULL); + ovpn_crypto_key_slot_put(ks); + } + + ks = rcu_access_pointer(cs->slots[1]); + if (ks) { + RCU_INIT_POINTER(cs->slots[1], NULL); + ovpn_crypto_key_slot_put(ks); + } +} + +/* Reset the ovpn_crypto_state object in a way that is atomic + * to RCU readers. + */ +int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs, + const struct ovpn_peer_key_reset *pkr) +{ + struct ovpn_crypto_key_slot *old = NULL, *new; + u8 idx; + + if (pkr->slot != OVPN_KEY_SLOT_PRIMARY && + pkr->slot != OVPN_KEY_SLOT_SECONDARY) + return -EINVAL; + + new = ovpn_aead_crypto_key_slot_new(&pkr->key); + if (IS_ERR(new)) + return PTR_ERR(new); + + spin_lock_bh(&cs->lock); + idx = cs->primary_idx; + switch (pkr->slot) { + case OVPN_KEY_SLOT_PRIMARY: + old = rcu_replace_pointer(cs->slots[idx], new, + lockdep_is_held(&cs->lock)); + break; + case OVPN_KEY_SLOT_SECONDARY: + old = rcu_replace_pointer(cs->slots[!idx], new, + lockdep_is_held(&cs->lock)); + break; + } + spin_unlock_bh(&cs->lock); + + if (old) + ovpn_crypto_key_slot_put(old); + + return 0; +} + +void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs, +enum ovpn_key_slot slot) +{ + struct ovpn_crypto_key_slot *ks = NULL; + u8 idx; + + if (slot != OVPN_KEY_SLOT_PRIMARY && + slot != OVPN_KEY_SLOT_SECONDARY) { + pr_warn("Invalid slot to release: %u\n", slot); + return; + } + + spin_lock_bh(&cs->lock); + idx = cs->primary_idx; + switch (slot) { + case OVPN_KEY_SLOT_PRIMARY: + ks = rcu_replace_pointer(cs->slots[idx], NULL,