diff options
Diffstat (limited to 'net/l2tp/l2tp_core.c')
-rw-r--r-- | net/l2tp/l2tp_core.c | 82 |
1 files changed, 65 insertions, 17 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 1a9f3723c13c..2ac884d0e89b 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c | |||
@@ -168,6 +168,51 @@ l2tp_session_id_hash_2(struct l2tp_net *pn, u32 session_id) | |||
168 | 168 | ||
169 | } | 169 | } |
170 | 170 | ||
171 | /* Lookup the tunnel socket, possibly involving the fs code if the socket is | ||
172 | * owned by userspace. A struct sock returned from this function must be | ||
173 | * released using l2tp_tunnel_sock_put once you're done with it. | ||
174 | */ | ||
175 | struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel) | ||
176 | { | ||
177 | int err = 0; | ||
178 | struct socket *sock = NULL; | ||
179 | struct sock *sk = NULL; | ||
180 | |||
181 | if (!tunnel) | ||
182 | goto out; | ||
183 | |||
184 | if (tunnel->fd >= 0) { | ||
185 | /* Socket is owned by userspace, who might be in the process | ||
186 | * of closing it. Look the socket up using the fd to ensure | ||
187 | * consistency. | ||
188 | */ | ||
189 | sock = sockfd_lookup(tunnel->fd, &err); | ||
190 | if (sock) | ||
191 | sk = sock->sk; | ||
192 | } else { | ||
193 | /* Socket is owned by kernelspace */ | ||
194 | sk = tunnel->sock; | ||
195 | } | ||
196 | |||
197 | out: | ||
198 | return sk; | ||
199 | } | ||
200 | EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_lookup); | ||
201 | |||
202 | /* Drop a reference to a tunnel socket obtained via. l2tp_tunnel_sock_put */ | ||
203 | void l2tp_tunnel_sock_put(struct sock *sk) | ||
204 | { | ||
205 | struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk); | ||
206 | if (tunnel) { | ||
207 | if (tunnel->fd >= 0) { | ||
208 | /* Socket is owned by userspace */ | ||
209 | sockfd_put(sk->sk_socket); | ||
210 | } | ||
211 | sock_put(sk); | ||
212 | } | ||
213 | } | ||
214 | EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_put); | ||
215 | |||
171 | /* Lookup a session by id in the global session list | 216 | /* Lookup a session by id in the global session list |
172 | */ | 217 | */ |
173 | static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id) | 218 | static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id) |
@@ -1123,8 +1168,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len | |||
1123 | struct udphdr *uh; | 1168 | struct udphdr *uh; |
1124 | struct inet_sock *inet; | 1169 | struct inet_sock *inet; |
1125 | __wsum csum; | 1170 | __wsum csum; |
1126 | int old_headroom; | ||
1127 | int new_headroom; | ||
1128 | int headroom; | 1171 | int headroom; |
1129 | int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; | 1172 | int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; |
1130 | int udp_len; | 1173 | int udp_len; |
@@ -1136,16 +1179,12 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len | |||
1136 | */ | 1179 | */ |
1137 | headroom = NET_SKB_PAD + sizeof(struct iphdr) + | 1180 | headroom = NET_SKB_PAD + sizeof(struct iphdr) + |
1138 | uhlen + hdr_len; | 1181 | uhlen + hdr_len; |
1139 | old_headroom = skb_headroom(skb); | ||
1140 | if (skb_cow_head(skb, headroom)) { | 1182 | if (skb_cow_head(skb, headroom)) { |
1141 | kfree_skb(skb); | 1183 | kfree_skb(skb); |
1142 | return NET_XMIT_DROP; | 1184 | return NET_XMIT_DROP; |
1143 | } | 1185 | } |
1144 | 1186 | ||
1145 | new_headroom = skb_headroom(skb); | ||
1146 | skb_orphan(skb); | 1187 | skb_orphan(skb); |
1147 | skb->truesize += new_headroom - old_headroom; | ||
1148 | |||
1149 | /* Setup L2TP header */ | 1188 | /* Setup L2TP header */ |
1150 | session->build_header(session, __skb_push(skb, hdr_len)); | 1189 | session->build_header(session, __skb_push(skb, hdr_len)); |
1151 | 1190 | ||
@@ -1607,6 +1646,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 | |||
1607 | tunnel->old_sk_destruct = sk->sk_destruct; | 1646 | tunnel->old_sk_destruct = sk->sk_destruct; |
1608 | sk->sk_destruct = &l2tp_tunnel_destruct; | 1647 | sk->sk_destruct = &l2tp_tunnel_destruct; |
1609 | tunnel->sock = sk; | 1648 | tunnel->sock = sk; |
1649 | tunnel->fd = fd; | ||
1610 | lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock"); | 1650 | lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock"); |
1611 | 1651 | ||
1612 | sk->sk_allocation = GFP_ATOMIC; | 1652 | sk->sk_allocation = GFP_ATOMIC; |
@@ -1642,24 +1682,32 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); | |||
1642 | */ | 1682 | */ |
1643 | int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) | 1683 | int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) |
1644 | { | 1684 | { |
1645 | int err = 0; | 1685 | int err = -EBADF; |
1646 | struct socket *sock = tunnel->sock ? tunnel->sock->sk_socket : NULL; | 1686 | struct socket *sock = NULL; |
1687 | struct sock *sk = NULL; | ||
1688 | |||
1689 | sk = l2tp_tunnel_sock_lookup(tunnel); | ||
1690 | if (!sk) | ||
1691 | goto out; | ||
1692 | |||
1693 | sock = sk->sk_socket; | ||
1694 | BUG_ON(!sock); | ||
1647 | 1695 | ||
1648 | /* Force the tunnel socket to close. This will eventually | 1696 | /* Force the tunnel socket to close. This will eventually |
1649 | * cause the tunnel to be deleted via the normal socket close | 1697 | * cause the tunnel to be deleted via the normal socket close |
1650 | * mechanisms when userspace closes the tunnel socket. | 1698 | * mechanisms when userspace closes the tunnel socket. |
1651 | */ | 1699 | */ |
1652 | if (sock != NULL) { | 1700 | err = inet_shutdown(sock, 2); |
1653 | err = inet_shutdown(sock, 2); | ||
1654 | 1701 | ||
1655 | /* If the tunnel's socket was created by the kernel, | 1702 | /* If the tunnel's socket was created by the kernel, |
1656 | * close the socket here since the socket was not | 1703 | * close the socket here since the socket was not |
1657 | * created by userspace. | 1704 | * created by userspace. |
1658 | */ | 1705 | */ |
1659 | if (sock->file == NULL) | 1706 | if (sock->file == NULL) |
1660 | err = inet_release(sock); | 1707 | err = inet_release(sock); |
1661 | } | ||
1662 | 1708 | ||
1709 | l2tp_tunnel_sock_put(sk); | ||
1710 | out: | ||
1663 | return err; | 1711 | return err; |
1664 | } | 1712 | } |
1665 | EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); | 1713 | EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); |