Implementation of full link layer processing for openvpn and other IP layer VPN

Source: Internet
Author: User
It would be nice if openvpn can also achieve the transmission mode VPN. If the VPN product based on openvpn can be connected to the user's network environment as an expensive network cable, it would be nice to automatically capture the traffic of interest; how nice is it to configure only one IP address to work without configuring any route.
We know that openvpn is a user-Mode Program. After receiving the Ethernet frame or IP datagram from the virtual network card by a character device, it is sent from the local device as the socket buffer. For the returned data, write the character device, simulate the virtual network interface card to receive the action, and then send data to the next hop through the routing result. The most important thing is routing. That is to say, if you deploy an openvpn-based VPN, therefore, it is inevitable to configure routes on the VPN. We know that routing configuration is basically a physical task, and it is not difficult, but once it is wrong, the consequences are very serious, therefore, Avoiding Route configuration becomes an important requirement. Can the system automatically identify the next hop using a mechanism? Before answering this question, we must understand the essence of the concept of "Next Hop". It has two meanings:
1. it pushes the IP datagram to the target, because the IP datagram is sent one by one;
2. It provides a basis for the link layer to actually send data.
If we do not consider virtual things, from the perspective of actually sending data, for Ethernet, the next hop is only used to obtain a target MAC address, then the link layer uses the MAC address as the target MAC address, and then the packet is still sent out.
At this point, everything becomes simple. We can obtain the source MAC address of a data frame in the original direction and cache it in the ip_conntrack structure. Then, we can use the cached MAC address as the target Mac to encapsulate the data frame for reply, and then send it directly without passing through the routing layer. The existence of Netfilter simplifies the actual implementation.
First, let's look at a graph. The graph shows a practical requirement:

The VPN endpoint has been built into a bridge. Since it is made into a bridge, you certainly do not want to configure routes on it. Then, the program needs to automatically store and identify the target MAC address of the data frame to be sent, then, the Mac is used to encapsulate the data. The following code implements this:

/** This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */# include <Linux/types. h> # include <Linux/netfilter. h> # include <Linux/module. h> # include <Linux/sysctl. h> # include <net/DST. h> # include <net/Netfilter/nf_conntrack.h> # include <net/Netfilter/nf_conntrack_core.h> struct Gwinfo {// automatically save the target MAC address of the Reply frame unsigned char reply_gw_mac [eth_alen]; // automatically save the original target MAC address unsigned char orig_gw_mac [eth_alen] of the forward frame; unsigned int local_flag; // automatically saves the device struct net_device * dev;}; static struct nf_ct_ext_type sggw_extend _ read_mostly = {. len = sizeof (struct gwinfo ),. align = _ alignof _ (struct gwinfo ),. id = nf_ct_ext_sggw,}; struct gwmark {_ u32 reply_mark; _ u32 orig_mark;}; struct gwmark g_ma Rk = {0x32, 0x32}; static unsigned int gwmark_target (struct sk_buff * SKB, const struct xt_target_param * par) {struct gwmark * GM = (struct gwmark *) par-> targinfo; g_mark.reply_mark = GM-> reply_mark; g_mark.orig_mark = GM-> orig_mark; return nf_accept;} static bool gwmark_check (const struct xt_target_param * par) {// todo return true;} static struct xt_target gwmark_tg _ read_mostly = {. name = "gwmark ",. Family = nfproto_ipv4 ,. target = gwmark_target ,. targetsize = sizeof (struct gwmark ),. table = "mangle ",. hooks = (1 <nf_inet_pre_routing) | (1 <nf_inet_local_out ),. checkentry = gwmark_check ,. me = this_module,}; static unsigned int =4_conntrack_get (unsigned int hooknum, struct sk_buff * SKB, const struct net_device * In, const struct net_device * Out, INT (* okfn) (struct sk_buff *) {Enum IP _ Conntrack_info ctinfo; struct gwinfo * ginfo = NULL; struct nf_conn * Ct = nf_ct_get (SKB, & ctinfo); If (! CT) {return nf_accept;} // ginfo management is only performed for the positive-direction package. If (ctinfo2dir (ctinfo) = ip_ct_dir_original | ctinfo = ip_ct_new) {// if it is a stream header package, set ginfo if (ctinfo = ip_ct_new) {ginfo = nf_ct_ext_add (CT, nf_ct_ext_sggw, gfp_atomic) of the stream );} else {// otherwise, ginfo = nf_ct_ext_find (CT, nf_ct_ext_sggw);} If (ginfo) {struct ethhdr * Eth = (struct ethhdr *) (SKB-> data-eth_hlen); // stores the source MAC address of the packet to automatically encapsulate the target Mac memcpy (G Info-> reply_gw_mac, Eth-> h_source, eth_alen); // the destination of the data packet to be stored in the user State (such as openvpn) re-encapsulate the target MAC address memcpy (ginfo-> orig_gw_mac, Eth-> h_dest, eth_alen); // Save the device variable because you want to directly send a data without routing, you must specify a device ginfo-> Dev = SKB-> dev; // The default data is not ginfo-> local_flag = 0;} return nf_accept ;} // This hook function is no big deal. It is designed to set the local flag static unsigned int limit 4_conntrack_get_local (unsigned int hooknum, struct sk_buff * SKB, const struct net_d Evice * In, const struct net_device * Out, INT (* okfn) (struct sk_buff *) {struct gwinfo * ginfo = NULL; Enum ip_conntrack_info ctinfo; struct nf_conn * Ct = nf_ct_get (SKB, & ctinfo); If (! CT) {return nf_accept;} define 4_conntrack_get (hooknum, SKB, in, out, okfn); If (ctinfo2dir (ctinfo) = ip_ct_dir_original | ctinfo = ip_ct_new) {ginfo = nf_ct_ext_find (CT, nf_ct_ext_sggw); If (ginfo) {ginfo-> local_flag = 1 ;}} return nf_accept ;} // The following hook functions automatically encapsulate ethereum frames without routing logic static unsigned int ipv4_conntrack_set (unsigned int hooknum, struct sk_buff * SKB, const struct net_device * In, const struct net_device * Out, INT (* okfn) (struct sk_buff *) {Enum ip_conntrack_info ctinfo; struct gwinfo * ginfo = NULL; struct net_device * Dev = NULL; unsigned int local = 0; struct nf_conn * Ct = nf_ct_get (SKB, & ctinfo); If (SKB-> MARK = g_mark.reply_mark & ctinfo2dir (ctinfo) ==ip_ct_dir_reply & hooknum = nf_inet_pre_routing) {ginfo = nf_ct_ext_find (CT, nf_ct_ext_sggw); If (ginfo) {Dev = ginfo-> dev ;}} else if (SKB-> MARK = g_mark.orig_mark & ctinfo2dir (ctinfo) = Export & hooknum = nf_inet_local_out) {ginfo = nf_ct_ext_find (CT, locate); If (ginfo) {struct dst_entry * DST = skb_dst (SKB); Dev = (struct net_device *) DST-> dev; Local = 1 ;}} if (Dev) {// the logic here is no longer obvious. Execute xmit directly on the prerouting Hook Point... struct ethhdr * Eth = (struct ethhdr *) SKB-> data-eth_hlen; If (local) {memcpy (ETH-> h_dest, ginfo-> orig_gw_mac, eth_alen );} else {memcpy (ETH-> h_dest, ginfo-> reply_gw_mac, eth_alen);} SKB-> Dev = dev; dev_queue_xmit (SKB); Return nf_stolen;} return nf_accept ;} static struct nf_hook_ops ipv4_conntrack_gwops [] _ read_mostly = {{. hook = ipv4_conntrack_get ,. owner = this_module ,. pF = nfproto_ipv4 ,. hooknum = nf_inet_pre_routing ,. priority = nf_ip_pri_conntrack + 1 ,},{. hook = ipv4_conntrack_get_local ,. owner = this_module ,. pF = nfproto_ipv4 ,. hooknum = nf_inet_local_out ,. priority = nf_ip_pri_conntrack + 1 ,},{. hook = ipv4_conntrack_set ,. owner = this_module ,. pF = nfproto_ipv4 ,. hooknum = nf_inet_pre_routing ,. priority = nf_ip_pri_mangle + 1,},}; // The init function is used as an example to register static int _ init nf_conntrack_sggw_init (void) {int ret = 0; ret = nf_register_hooks (bytes, array_size (ipv4_conntrack_gwops); If (Ret <0) {printk ("nf_conntrack_ipv4: Can't register GW hooks. \ n ");} ret = nf_ct_extend_register (& sggw_extend); // register if (Ret <0) {printk (kern_err" sggw: unable to register extension \ n "); return ret;} ret = xt_register_target (& gwmark_tg); If (Ret <0) {printk (kern_err "sggw: unable to register target \ n"); return ret ;} return ret;} // The fini function is used as an example to describe static void _ exit register (void) {synchronize_net (); Register (pai4_conntrack_gwops, array_size (pai4_conntrack_gwops )); nf_ct_extend_unregister (& sggw_extend); xt_unregister_target (& gwmark_tg);} module_init (modules); module_exit (modules); module_alias ("sggw"); module_license ("GPL ");

In addition to the above kernel module code, an iptables program is also required to set two marks. I did not write this user mode module, but used other methods to achieve the goal. I am so busy recently that I can't start or end up doing things any more... the above modules have been tested and run well, but I did not take it into consideration, that is, the above Code has not implemented any MAC address change's notify mechanism, therefore, it is only suitable for Ethernet environments with stable physical locations. Hot Standby switching of upstream routers may cause problems. However, if they share a MAC address in the hot standby group, I am so sorry, however, vrrp/HSRP logs are not here !!
I was humiliated during product implementation. I recently want to implement a VPN based on openvpn. Anyone can insult me, but cannot insult my product!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.