|
9 | 9 |
|
10 | 10 | #include <linux/netdevice.h> |
11 | 11 | #include <linux/skbuff.h> |
| 12 | +#include <net/gso.h> |
12 | 13 |
|
13 | 14 | #include "io.h" |
| 15 | +#include "ovpnpriv.h" |
| 16 | +#include "peer.h" |
| 17 | +#include "udp.h" |
| 18 | +#include "skb.h" |
| 19 | +#include "socket.h" |
| 20 | + |
| 21 | +static void ovpn_encrypt_post(struct sk_buff *skb, int ret) |
| 22 | +{ |
| 23 | + struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer; |
| 24 | + struct ovpn_socket *sock; |
| 25 | + |
| 26 | + if (unlikely(ret < 0)) |
| 27 | + goto err; |
| 28 | + |
| 29 | + skb_mark_not_on_list(skb); |
| 30 | + |
| 31 | + rcu_read_lock(); |
| 32 | + sock = rcu_dereference(peer->sock); |
| 33 | + if (unlikely(!sock)) |
| 34 | + goto err_unlock; |
| 35 | + |
| 36 | + switch (sock->sock->sk->sk_protocol) { |
| 37 | + case IPPROTO_UDP: |
| 38 | + ovpn_udp_send_skb(peer, sock->sock, skb); |
| 39 | + break; |
| 40 | + default: |
| 41 | + /* no transport configured yet */ |
| 42 | + goto err_unlock; |
| 43 | + } |
| 44 | + /* skb passed down the stack - don't free it */ |
| 45 | + skb = NULL; |
| 46 | +err_unlock: |
| 47 | + rcu_read_unlock(); |
| 48 | +err: |
| 49 | + if (unlikely(skb)) |
| 50 | + dev_dstats_tx_dropped(peer->ovpn->dev); |
| 51 | + ovpn_peer_put(peer); |
| 52 | + kfree_skb(skb); |
| 53 | +} |
| 54 | + |
| 55 | +static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb) |
| 56 | +{ |
| 57 | + ovpn_skb_cb(skb)->peer = peer; |
| 58 | + |
| 59 | + /* take a reference to the peer because the crypto code may run async. |
| 60 | + * ovpn_encrypt_post() will release it upon completion |
| 61 | + */ |
| 62 | + if (unlikely(!ovpn_peer_hold(peer))) { |
| 63 | + DEBUG_NET_WARN_ON_ONCE(1); |
| 64 | + return false; |
| 65 | + } |
| 66 | + |
| 67 | + ovpn_encrypt_post(skb, 0); |
| 68 | + return true; |
| 69 | +} |
| 70 | + |
| 71 | +/* send skb to connected peer, if any */ |
| 72 | +static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb, |
| 73 | + struct ovpn_peer *peer) |
| 74 | +{ |
| 75 | + struct sk_buff *curr, *next; |
| 76 | + |
| 77 | + /* this might be a GSO-segmented skb list: process each skb |
| 78 | + * independently |
| 79 | + */ |
| 80 | + skb_list_walk_safe(skb, curr, next) { |
| 81 | + if (unlikely(!ovpn_encrypt_one(peer, curr))) { |
| 82 | + dev_dstats_tx_dropped(ovpn->dev); |
| 83 | + kfree_skb(curr); |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + ovpn_peer_put(peer); |
| 88 | +} |
14 | 89 |
|
15 | 90 | /* Send user data to the network |
16 | 91 | */ |
17 | 92 | netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) |
18 | 93 | { |
| 94 | + struct ovpn_priv *ovpn = netdev_priv(dev); |
| 95 | + struct sk_buff *segments, *curr, *next; |
| 96 | + struct sk_buff_head skb_list; |
| 97 | + struct ovpn_peer *peer; |
| 98 | + __be16 proto; |
| 99 | + int ret; |
| 100 | + |
| 101 | + /* reset netfilter state */ |
| 102 | + nf_reset_ct(skb); |
| 103 | + |
| 104 | + /* verify IP header size in network packet */ |
| 105 | + proto = ovpn_ip_check_protocol(skb); |
| 106 | + if (unlikely(!proto || skb->protocol != proto)) |
| 107 | + goto drop; |
| 108 | + |
| 109 | + if (skb_is_gso(skb)) { |
| 110 | + segments = skb_gso_segment(skb, 0); |
| 111 | + if (IS_ERR(segments)) { |
| 112 | + ret = PTR_ERR(segments); |
| 113 | + net_err_ratelimited("%s: cannot segment payload packet: %d\n", |
| 114 | + netdev_name(dev), ret); |
| 115 | + goto drop; |
| 116 | + } |
| 117 | + |
| 118 | + consume_skb(skb); |
| 119 | + skb = segments; |
| 120 | + } |
| 121 | + |
| 122 | + /* from this moment on, "skb" might be a list */ |
| 123 | + |
| 124 | + __skb_queue_head_init(&skb_list); |
| 125 | + skb_list_walk_safe(skb, curr, next) { |
| 126 | + skb_mark_not_on_list(curr); |
| 127 | + |
| 128 | + curr = skb_share_check(curr, GFP_ATOMIC); |
| 129 | + if (unlikely(!curr)) { |
| 130 | + net_err_ratelimited("%s: skb_share_check failed for payload packet\n", |
| 131 | + netdev_name(dev)); |
| 132 | + dev_dstats_tx_dropped(ovpn->dev); |
| 133 | + continue; |
| 134 | + } |
| 135 | + |
| 136 | + __skb_queue_tail(&skb_list, curr); |
| 137 | + } |
| 138 | + skb_list.prev->next = NULL; |
| 139 | + |
| 140 | + /* retrieve peer serving the destination IP of this packet */ |
| 141 | + peer = ovpn_peer_get_by_dst(ovpn, skb); |
| 142 | + if (unlikely(!peer)) { |
| 143 | + net_dbg_ratelimited("%s: no peer to send data to\n", |
| 144 | + netdev_name(ovpn->dev)); |
| 145 | + goto drop; |
| 146 | + } |
| 147 | + |
| 148 | + ovpn_send(ovpn, skb_list.next, peer); |
| 149 | + |
| 150 | + return NETDEV_TX_OK; |
| 151 | + |
| 152 | +drop: |
| 153 | + dev_dstats_tx_dropped(ovpn->dev); |
19 | 154 | skb_tx_error(skb); |
20 | | - kfree_skb(skb); |
| 155 | + kfree_skb_list(skb); |
21 | 156 | return NET_XMIT_DROP; |
22 | 157 | } |
0 commit comments