aboutsummaryrefslogtreecommitdiffstats
path: root/net/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/socket.c')
-rw-r--r--net/socket.c46
1 files changed, 46 insertions, 0 deletions
diff --git a/net/socket.c b/net/socket.c
index 985ef06792d6..c2564eb25c6b 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -3356,3 +3356,49 @@ int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how)
3356 return sock->ops->shutdown(sock, how); 3356 return sock->ops->shutdown(sock, how);
3357} 3357}
3358EXPORT_SYMBOL(kernel_sock_shutdown); 3358EXPORT_SYMBOL(kernel_sock_shutdown);
3359
3360/* This routine returns the IP overhead imposed by a socket i.e.
3361 * the length of the underlying IP header, depending on whether
3362 * this is an IPv4 or IPv6 socket and the length from IP options turned
3363 * on at the socket. Assumes that the caller has a lock on the socket.
3364 */
3365u32 kernel_sock_ip_overhead(struct sock *sk)
3366{
3367 struct inet_sock *inet;
3368 struct ip_options_rcu *opt;
3369 u32 overhead = 0;
3370 bool owned_by_user;
3371#if IS_ENABLED(CONFIG_IPV6)
3372 struct ipv6_pinfo *np;
3373 struct ipv6_txoptions *optv6 = NULL;
3374#endif /* IS_ENABLED(CONFIG_IPV6) */
3375
3376 if (!sk)
3377 return overhead;
3378
3379 owned_by_user = sock_owned_by_user(sk);
3380 switch (sk->sk_family) {
3381 case AF_INET:
3382 inet = inet_sk(sk);
3383 overhead += sizeof(struct iphdr);
3384 opt = rcu_dereference_protected(inet->inet_opt,
3385 owned_by_user);
3386 if (opt)
3387 overhead += opt->opt.optlen;
3388 return overhead;
3389#if IS_ENABLED(CONFIG_IPV6)
3390 case AF_INET6:
3391 np = inet6_sk(sk);
3392 overhead += sizeof(struct ipv6hdr);
3393 if (np)
3394 optv6 = rcu_dereference_protected(np->opt,
3395 owned_by_user);
3396 if (optv6)
3397 overhead += (optv6->opt_flen + optv6->opt_nflen);
3398 return overhead;
3399#endif /* IS_ENABLED(CONFIG_IPV6) */
3400 default: /* Returns 0 overhead if the socket is not ipv4 or ipv6 */
3401 return overhead;
3402 }
3403}
3404EXPORT_SYMBOL(kernel_sock_ip_overhead);