Merge branch 'net-first-round-to-use-dev_net_rcu'
Eric Dumazet says: ==================== net: first round to use dev_net_rcu() dev_net(dev) should either be protected by RTNL or RCU. There is no LOCKDEP support yet for this helper. Adding it would trigger too many splats. Instead, add dev_net_rcu() for rcu_read_lock() contexts and start to use it to fix bugs and clearly document the safety requirements. v4: https://lore.kernel.org/CANn89i+AozhFhZNK0Y4e_EqXV1=yKjGuvf43Wa6JJKWMOixWQQ@mail.gmail.com v3: https://lore.kernel.org/20250203153633.46ce0337@kernel.org/ ==================== Link: https://patch.msgid.link/20250205155120.1676781-1-edumazet@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
3da81cb9a4
11 changed files with 113 additions and 65 deletions
|
@ -2663,6 +2663,12 @@ struct net *dev_net(const struct net_device *dev)
|
||||||
return read_pnet(&dev->nd_net);
|
return read_pnet(&dev->nd_net);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
struct net *dev_net_rcu(const struct net_device *dev)
|
||||||
|
{
|
||||||
|
return read_pnet_rcu(&dev->nd_net);
|
||||||
|
}
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
void dev_net_set(struct net_device *dev, struct net *net)
|
void dev_net_set(struct net_device *dev, struct net *net)
|
||||||
{
|
{
|
||||||
|
|
|
@ -471,9 +471,12 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
|
||||||
bool forwarding)
|
bool forwarding)
|
||||||
{
|
{
|
||||||
const struct rtable *rt = dst_rtable(dst);
|
const struct rtable *rt = dst_rtable(dst);
|
||||||
struct net *net = dev_net(dst->dev);
|
unsigned int mtu, res;
|
||||||
unsigned int mtu;
|
struct net *net;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
net = dev_net_rcu(dst->dev);
|
||||||
if (READ_ONCE(net->ipv4.sysctl_ip_fwd_use_pmtu) ||
|
if (READ_ONCE(net->ipv4.sysctl_ip_fwd_use_pmtu) ||
|
||||||
ip_mtu_locked(dst) ||
|
ip_mtu_locked(dst) ||
|
||||||
!forwarding) {
|
!forwarding) {
|
||||||
|
@ -497,7 +500,11 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
|
||||||
out:
|
out:
|
||||||
mtu = min_t(unsigned int, mtu, IP_MAX_MTU);
|
mtu = min_t(unsigned int, mtu, IP_MAX_MTU);
|
||||||
|
|
||||||
return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
|
res = mtu - lwtunnel_headroom(dst->lwtstate, mtu);
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
|
static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
|
||||||
|
|
|
@ -398,7 +398,7 @@ static inline struct net *read_pnet(const possible_net_t *pnet)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct net *read_pnet_rcu(possible_net_t *pnet)
|
static inline struct net *read_pnet_rcu(const possible_net_t *pnet)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_NET_NS
|
#ifdef CONFIG_NET_NS
|
||||||
return rcu_dereference(pnet->net);
|
return rcu_dereference(pnet->net);
|
||||||
|
|
|
@ -382,10 +382,15 @@ static inline int inet_iif(const struct sk_buff *skb)
|
||||||
static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
|
static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
|
||||||
{
|
{
|
||||||
int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
|
int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
|
||||||
struct net *net = dev_net(dst->dev);
|
|
||||||
|
|
||||||
if (hoplimit == 0)
|
if (hoplimit == 0) {
|
||||||
|
const struct net *net;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
net = dev_net_rcu(dst->dev);
|
||||||
hoplimit = READ_ONCE(net->ipv4.sysctl_ip_default_ttl);
|
hoplimit = READ_ONCE(net->ipv4.sysctl_ip_default_ttl);
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
return hoplimit;
|
return hoplimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1108,10 +1108,12 @@ bool __skb_flow_dissect(const struct net *net,
|
||||||
FLOW_DISSECTOR_KEY_BASIC,
|
FLOW_DISSECTOR_KEY_BASIC,
|
||||||
target_container);
|
target_container);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
if (skb) {
|
if (skb) {
|
||||||
if (!net) {
|
if (!net) {
|
||||||
if (skb->dev)
|
if (skb->dev)
|
||||||
net = dev_net(skb->dev);
|
net = dev_net_rcu(skb->dev);
|
||||||
else if (skb->sk)
|
else if (skb->sk)
|
||||||
net = sock_net(skb->sk);
|
net = sock_net(skb->sk);
|
||||||
}
|
}
|
||||||
|
@ -1122,7 +1124,6 @@ bool __skb_flow_dissect(const struct net *net,
|
||||||
enum netns_bpf_attach_type type = NETNS_BPF_FLOW_DISSECTOR;
|
enum netns_bpf_attach_type type = NETNS_BPF_FLOW_DISSECTOR;
|
||||||
struct bpf_prog_array *run_array;
|
struct bpf_prog_array *run_array;
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
run_array = rcu_dereference(init_net.bpf.run_array[type]);
|
run_array = rcu_dereference(init_net.bpf.run_array[type]);
|
||||||
if (!run_array)
|
if (!run_array)
|
||||||
run_array = rcu_dereference(net->bpf.run_array[type]);
|
run_array = rcu_dereference(net->bpf.run_array[type]);
|
||||||
|
@ -1150,17 +1151,17 @@ bool __skb_flow_dissect(const struct net *net,
|
||||||
prog = READ_ONCE(run_array->items[0].prog);
|
prog = READ_ONCE(run_array->items[0].prog);
|
||||||
result = bpf_flow_dissect(prog, &ctx, n_proto, nhoff,
|
result = bpf_flow_dissect(prog, &ctx, n_proto, nhoff,
|
||||||
hlen, flags);
|
hlen, flags);
|
||||||
if (result == BPF_FLOW_DISSECTOR_CONTINUE)
|
if (result != BPF_FLOW_DISSECTOR_CONTINUE) {
|
||||||
goto dissect_continue;
|
__skb_flow_bpf_to_target(&flow_keys, flow_dissector,
|
||||||
__skb_flow_bpf_to_target(&flow_keys, flow_dissector,
|
target_container);
|
||||||
target_container);
|
rcu_read_unlock();
|
||||||
rcu_read_unlock();
|
return result == BPF_OK;
|
||||||
return result == BPF_OK;
|
}
|
||||||
}
|
}
|
||||||
dissect_continue:
|
|
||||||
rcu_read_unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (dissector_uses_key(flow_dissector,
|
if (dissector_uses_key(flow_dissector,
|
||||||
FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
|
||||||
struct ethhdr *eth = eth_hdr(skb);
|
struct ethhdr *eth = eth_hdr(skb);
|
||||||
|
|
|
@ -1371,10 +1371,11 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
|
||||||
__be32 addr = 0;
|
__be32 addr = 0;
|
||||||
unsigned char localnet_scope = RT_SCOPE_HOST;
|
unsigned char localnet_scope = RT_SCOPE_HOST;
|
||||||
struct in_device *in_dev;
|
struct in_device *in_dev;
|
||||||
struct net *net = dev_net(dev);
|
struct net *net;
|
||||||
int master_idx;
|
int master_idx;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
|
net = dev_net_rcu(dev);
|
||||||
in_dev = __in_dev_get_rcu(dev);
|
in_dev = __in_dev_get_rcu(dev);
|
||||||
if (!in_dev)
|
if (!in_dev)
|
||||||
goto no_in_dev;
|
goto no_in_dev;
|
||||||
|
|
|
@ -399,10 +399,10 @@ static void icmp_push_reply(struct sock *sk,
|
||||||
|
|
||||||
static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
|
static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct ipcm_cookie ipc;
|
|
||||||
struct rtable *rt = skb_rtable(skb);
|
struct rtable *rt = skb_rtable(skb);
|
||||||
struct net *net = dev_net(rt->dst.dev);
|
struct net *net = dev_net_rcu(rt->dst.dev);
|
||||||
bool apply_ratelimit = false;
|
bool apply_ratelimit = false;
|
||||||
|
struct ipcm_cookie ipc;
|
||||||
struct flowi4 fl4;
|
struct flowi4 fl4;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct inet_sock *inet;
|
struct inet_sock *inet;
|
||||||
|
@ -608,12 +608,14 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
|
|
||||||
if (!rt)
|
if (!rt)
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
if (rt->dst.dev)
|
if (rt->dst.dev)
|
||||||
net = dev_net(rt->dst.dev);
|
net = dev_net_rcu(rt->dst.dev);
|
||||||
else if (skb_in->dev)
|
else if (skb_in->dev)
|
||||||
net = dev_net(skb_in->dev);
|
net = dev_net_rcu(skb_in->dev);
|
||||||
else
|
else
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -785,7 +787,8 @@ out_unlock:
|
||||||
icmp_xmit_unlock(sk);
|
icmp_xmit_unlock(sk);
|
||||||
out_bh_enable:
|
out_bh_enable:
|
||||||
local_bh_enable();
|
local_bh_enable();
|
||||||
out:;
|
out:
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__icmp_send);
|
EXPORT_SYMBOL(__icmp_send);
|
||||||
|
|
||||||
|
@ -834,7 +837,7 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
|
||||||
* avoid additional coding at protocol handlers.
|
* avoid additional coding at protocol handlers.
|
||||||
*/
|
*/
|
||||||
if (!pskb_may_pull(skb, iph->ihl * 4 + 8)) {
|
if (!pskb_may_pull(skb, iph->ihl * 4 + 8)) {
|
||||||
__ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
|
__ICMP_INC_STATS(dev_net_rcu(skb->dev), ICMP_MIB_INERRORS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,7 +871,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
|
||||||
struct net *net;
|
struct net *net;
|
||||||
u32 info = 0;
|
u32 info = 0;
|
||||||
|
|
||||||
net = dev_net(skb_dst(skb)->dev);
|
net = dev_net_rcu(skb_dst(skb)->dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Incomplete header ?
|
* Incomplete header ?
|
||||||
|
@ -979,7 +982,7 @@ out_err:
|
||||||
static enum skb_drop_reason icmp_redirect(struct sk_buff *skb)
|
static enum skb_drop_reason icmp_redirect(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
if (skb->len < sizeof(struct iphdr)) {
|
if (skb->len < sizeof(struct iphdr)) {
|
||||||
__ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
|
__ICMP_INC_STATS(dev_net_rcu(skb->dev), ICMP_MIB_INERRORS);
|
||||||
return SKB_DROP_REASON_PKT_TOO_SMALL;
|
return SKB_DROP_REASON_PKT_TOO_SMALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1011,7 +1014,7 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
|
||||||
struct icmp_bxm icmp_param;
|
struct icmp_bxm icmp_param;
|
||||||
struct net *net;
|
struct net *net;
|
||||||
|
|
||||||
net = dev_net(skb_dst(skb)->dev);
|
net = dev_net_rcu(skb_dst(skb)->dev);
|
||||||
/* should there be an ICMP stat for ignored echos? */
|
/* should there be an ICMP stat for ignored echos? */
|
||||||
if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all))
|
if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all))
|
||||||
return SKB_NOT_DROPPED_YET;
|
return SKB_NOT_DROPPED_YET;
|
||||||
|
@ -1040,9 +1043,9 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
|
||||||
|
|
||||||
bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
|
bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
|
||||||
{
|
{
|
||||||
|
struct net *net = dev_net_rcu(skb->dev);
|
||||||
struct icmp_ext_hdr *ext_hdr, _ext_hdr;
|
struct icmp_ext_hdr *ext_hdr, _ext_hdr;
|
||||||
struct icmp_ext_echo_iio *iio, _iio;
|
struct icmp_ext_echo_iio *iio, _iio;
|
||||||
struct net *net = dev_net(skb->dev);
|
|
||||||
struct inet6_dev *in6_dev;
|
struct inet6_dev *in6_dev;
|
||||||
struct in_device *in_dev;
|
struct in_device *in_dev;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
@ -1181,7 +1184,7 @@ static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb)
|
||||||
return SKB_NOT_DROPPED_YET;
|
return SKB_NOT_DROPPED_YET;
|
||||||
|
|
||||||
out_err:
|
out_err:
|
||||||
__ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
|
__ICMP_INC_STATS(dev_net_rcu(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
|
||||||
return SKB_DROP_REASON_PKT_TOO_SMALL;
|
return SKB_DROP_REASON_PKT_TOO_SMALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1198,7 +1201,7 @@ int icmp_rcv(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
|
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
|
||||||
struct rtable *rt = skb_rtable(skb);
|
struct rtable *rt = skb_rtable(skb);
|
||||||
struct net *net = dev_net(rt->dst.dev);
|
struct net *net = dev_net_rcu(rt->dst.dev);
|
||||||
struct icmphdr *icmph;
|
struct icmphdr *icmph;
|
||||||
|
|
||||||
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
|
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
|
||||||
|
@ -1371,9 +1374,9 @@ int icmp_err(struct sk_buff *skb, u32 info)
|
||||||
struct iphdr *iph = (struct iphdr *)skb->data;
|
struct iphdr *iph = (struct iphdr *)skb->data;
|
||||||
int offset = iph->ihl<<2;
|
int offset = iph->ihl<<2;
|
||||||
struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
|
struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
|
||||||
|
struct net *net = dev_net_rcu(skb->dev);
|
||||||
int type = icmp_hdr(skb)->type;
|
int type = icmp_hdr(skb)->type;
|
||||||
int code = icmp_hdr(skb)->code;
|
int code = icmp_hdr(skb)->code;
|
||||||
struct net *net = dev_net(skb->dev);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use ping_err to handle all icmp errors except those
|
* Use ping_err to handle all icmp errors except those
|
||||||
|
|
|
@ -390,7 +390,13 @@ static inline int ip_rt_proc_init(void)
|
||||||
|
|
||||||
static inline bool rt_is_expired(const struct rtable *rth)
|
static inline bool rt_is_expired(const struct rtable *rth)
|
||||||
{
|
{
|
||||||
return rth->rt_genid != rt_genid_ipv4(dev_net(rth->dst.dev));
|
bool res;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
res = rth->rt_genid != rt_genid_ipv4(dev_net_rcu(rth->dst.dev));
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rt_cache_flush(struct net *net)
|
void rt_cache_flush(struct net *net)
|
||||||
|
@ -1002,9 +1008,9 @@ out: kfree_skb_reason(skb, reason);
|
||||||
static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
|
static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
|
||||||
{
|
{
|
||||||
struct dst_entry *dst = &rt->dst;
|
struct dst_entry *dst = &rt->dst;
|
||||||
struct net *net = dev_net(dst->dev);
|
|
||||||
struct fib_result res;
|
struct fib_result res;
|
||||||
bool lock = false;
|
bool lock = false;
|
||||||
|
struct net *net;
|
||||||
u32 old_mtu;
|
u32 old_mtu;
|
||||||
|
|
||||||
if (ip_mtu_locked(dst))
|
if (ip_mtu_locked(dst))
|
||||||
|
@ -1014,6 +1020,8 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
|
||||||
if (old_mtu < mtu)
|
if (old_mtu < mtu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
net = dev_net_rcu(dst->dev);
|
||||||
if (mtu < net->ipv4.ip_rt_min_pmtu) {
|
if (mtu < net->ipv4.ip_rt_min_pmtu) {
|
||||||
lock = true;
|
lock = true;
|
||||||
mtu = min(old_mtu, net->ipv4.ip_rt_min_pmtu);
|
mtu = min(old_mtu, net->ipv4.ip_rt_min_pmtu);
|
||||||
|
@ -1021,9 +1029,8 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
|
||||||
|
|
||||||
if (rt->rt_pmtu == mtu && !lock &&
|
if (rt->rt_pmtu == mtu && !lock &&
|
||||||
time_before(jiffies, dst->expires - net->ipv4.ip_rt_mtu_expires / 2))
|
time_before(jiffies, dst->expires - net->ipv4.ip_rt_mtu_expires / 2))
|
||||||
return;
|
goto out;
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
if (fib_lookup(net, fl4, &res, 0) == 0) {
|
if (fib_lookup(net, fl4, &res, 0) == 0) {
|
||||||
struct fib_nh_common *nhc;
|
struct fib_nh_common *nhc;
|
||||||
|
|
||||||
|
@ -1037,14 +1044,14 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
|
||||||
update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
|
update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
|
||||||
jiffies + net->ipv4.ip_rt_mtu_expires);
|
jiffies + net->ipv4.ip_rt_mtu_expires);
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
goto out;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
|
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
|
||||||
nhc = FIB_RES_NHC(res);
|
nhc = FIB_RES_NHC(res);
|
||||||
update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
|
update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
|
||||||
jiffies + net->ipv4.ip_rt_mtu_expires);
|
jiffies + net->ipv4.ip_rt_mtu_expires);
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1307,10 +1314,15 @@ static void set_class_tag(struct rtable *rt, u32 tag)
|
||||||
|
|
||||||
static unsigned int ipv4_default_advmss(const struct dst_entry *dst)
|
static unsigned int ipv4_default_advmss(const struct dst_entry *dst)
|
||||||
{
|
{
|
||||||
struct net *net = dev_net(dst->dev);
|
|
||||||
unsigned int header_size = sizeof(struct tcphdr) + sizeof(struct iphdr);
|
unsigned int header_size = sizeof(struct tcphdr) + sizeof(struct iphdr);
|
||||||
unsigned int advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
|
unsigned int advmss;
|
||||||
net->ipv4.ip_rt_min_advmss);
|
struct net *net;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
net = dev_net_rcu(dst->dev);
|
||||||
|
advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
|
||||||
|
net->ipv4.ip_rt_min_advmss);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
return min(advmss, IPV4_MAX_PMTU - header_size);
|
return min(advmss, IPV4_MAX_PMTU - header_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ static int icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||||
{
|
{
|
||||||
/* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */
|
/* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */
|
||||||
struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset);
|
struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset);
|
||||||
struct net *net = dev_net(skb->dev);
|
struct net *net = dev_net_rcu(skb->dev);
|
||||||
|
|
||||||
if (type == ICMPV6_PKT_TOOBIG)
|
if (type == ICMPV6_PKT_TOOBIG)
|
||||||
ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0, sock_net_uid(net, NULL));
|
ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0, sock_net_uid(net, NULL));
|
||||||
|
@ -473,7 +473,10 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
|
|
||||||
if (!skb->dev)
|
if (!skb->dev)
|
||||||
return;
|
return;
|
||||||
net = dev_net(skb->dev);
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
net = dev_net_rcu(skb->dev);
|
||||||
mark = IP6_REPLY_MARK(net, skb->mark);
|
mark = IP6_REPLY_MARK(net, skb->mark);
|
||||||
/*
|
/*
|
||||||
* Make sure we respect the rules
|
* Make sure we respect the rules
|
||||||
|
@ -496,7 +499,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
!(type == ICMPV6_PARAMPROB &&
|
!(type == ICMPV6_PARAMPROB &&
|
||||||
code == ICMPV6_UNK_OPTION &&
|
code == ICMPV6_UNK_OPTION &&
|
||||||
(opt_unrec(skb, info))))
|
(opt_unrec(skb, info))))
|
||||||
return;
|
goto out;
|
||||||
|
|
||||||
saddr = NULL;
|
saddr = NULL;
|
||||||
}
|
}
|
||||||
|
@ -526,7 +529,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
|
if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
|
||||||
net_dbg_ratelimited("icmp6_send: addr_any/mcast source [%pI6c > %pI6c]\n",
|
net_dbg_ratelimited("icmp6_send: addr_any/mcast source [%pI6c > %pI6c]\n",
|
||||||
&hdr->saddr, &hdr->daddr);
|
&hdr->saddr, &hdr->daddr);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -535,7 +538,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
if (is_ineligible(skb)) {
|
if (is_ineligible(skb)) {
|
||||||
net_dbg_ratelimited("icmp6_send: no reply to icmp error [%pI6c > %pI6c]\n",
|
net_dbg_ratelimited("icmp6_send: no reply to icmp error [%pI6c > %pI6c]\n",
|
||||||
&hdr->saddr, &hdr->daddr);
|
&hdr->saddr, &hdr->daddr);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Needed by both icmpv6_global_allow and icmpv6_xmit_lock */
|
/* Needed by both icmpv6_global_allow and icmpv6_xmit_lock */
|
||||||
|
@ -582,7 +585,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
np = inet6_sk(sk);
|
np = inet6_sk(sk);
|
||||||
|
|
||||||
if (!icmpv6_xrlim_allow(sk, type, &fl6, apply_ratelimit))
|
if (!icmpv6_xrlim_allow(sk, type, &fl6, apply_ratelimit))
|
||||||
goto out;
|
goto out_unlock;
|
||||||
|
|
||||||
tmp_hdr.icmp6_type = type;
|
tmp_hdr.icmp6_type = type;
|
||||||
tmp_hdr.icmp6_code = code;
|
tmp_hdr.icmp6_code = code;
|
||||||
|
@ -600,7 +603,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
|
|
||||||
dst = icmpv6_route_lookup(net, skb, sk, &fl6);
|
dst = icmpv6_route_lookup(net, skb, sk, &fl6);
|
||||||
if (IS_ERR(dst))
|
if (IS_ERR(dst))
|
||||||
goto out;
|
goto out_unlock;
|
||||||
|
|
||||||
ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
|
ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
|
||||||
|
|
||||||
|
@ -616,7 +619,6 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
goto out_dst_release;
|
goto out_dst_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
idev = __in6_dev_get(skb->dev);
|
idev = __in6_dev_get(skb->dev);
|
||||||
|
|
||||||
if (ip6_append_data(sk, icmpv6_getfrag, &msg,
|
if (ip6_append_data(sk, icmpv6_getfrag, &msg,
|
||||||
|
@ -630,13 +632,15 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||||
icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
|
icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
|
||||||
len + sizeof(struct icmp6hdr));
|
len + sizeof(struct icmp6hdr));
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
out_dst_release:
|
out_dst_release:
|
||||||
dst_release(dst);
|
dst_release(dst);
|
||||||
out:
|
out_unlock:
|
||||||
icmpv6_xmit_unlock(sk);
|
icmpv6_xmit_unlock(sk);
|
||||||
out_bh_enable:
|
out_bh_enable:
|
||||||
local_bh_enable();
|
local_bh_enable();
|
||||||
|
out:
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(icmp6_send);
|
EXPORT_SYMBOL(icmp6_send);
|
||||||
|
|
||||||
|
@ -679,8 +683,8 @@ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
|
||||||
skb_pull(skb2, nhs);
|
skb_pull(skb2, nhs);
|
||||||
skb_reset_network_header(skb2);
|
skb_reset_network_header(skb2);
|
||||||
|
|
||||||
rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr, NULL, 0,
|
rt = rt6_lookup(dev_net_rcu(skb->dev), &ipv6_hdr(skb2)->saddr,
|
||||||
skb, 0);
|
NULL, 0, skb, 0);
|
||||||
|
|
||||||
if (rt && rt->dst.dev)
|
if (rt && rt->dst.dev)
|
||||||
skb2->dev = rt->dst.dev;
|
skb2->dev = rt->dst.dev;
|
||||||
|
@ -717,7 +721,7 @@ EXPORT_SYMBOL(ip6_err_gen_icmpv6_unreach);
|
||||||
|
|
||||||
static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
|
static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct net *net = dev_net(skb->dev);
|
struct net *net = dev_net_rcu(skb->dev);
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct inet6_dev *idev;
|
struct inet6_dev *idev;
|
||||||
struct ipv6_pinfo *np;
|
struct ipv6_pinfo *np;
|
||||||
|
@ -832,7 +836,7 @@ enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,
|
||||||
u8 code, __be32 info)
|
u8 code, __be32 info)
|
||||||
{
|
{
|
||||||
struct inet6_skb_parm *opt = IP6CB(skb);
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
||||||
struct net *net = dev_net(skb->dev);
|
struct net *net = dev_net_rcu(skb->dev);
|
||||||
const struct inet6_protocol *ipprot;
|
const struct inet6_protocol *ipprot;
|
||||||
enum skb_drop_reason reason;
|
enum skb_drop_reason reason;
|
||||||
int inner_offset;
|
int inner_offset;
|
||||||
|
@ -889,7 +893,7 @@ out:
|
||||||
static int icmpv6_rcv(struct sk_buff *skb)
|
static int icmpv6_rcv(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
|
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
|
||||||
struct net *net = dev_net(skb->dev);
|
struct net *net = dev_net_rcu(skb->dev);
|
||||||
struct net_device *dev = icmp6_dev(skb);
|
struct net_device *dev = icmp6_dev(skb);
|
||||||
struct inet6_dev *idev = __in6_dev_get(dev);
|
struct inet6_dev *idev = __in6_dev_get(dev);
|
||||||
const struct in6_addr *saddr, *daddr;
|
const struct in6_addr *saddr, *daddr;
|
||||||
|
@ -921,7 +925,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
|
||||||
skb_set_network_header(skb, nh);
|
skb_set_network_header(skb, nh);
|
||||||
}
|
}
|
||||||
|
|
||||||
__ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INMSGS);
|
__ICMP6_INC_STATS(dev_net_rcu(dev), idev, ICMP6_MIB_INMSGS);
|
||||||
|
|
||||||
saddr = &ipv6_hdr(skb)->saddr;
|
saddr = &ipv6_hdr(skb)->saddr;
|
||||||
daddr = &ipv6_hdr(skb)->daddr;
|
daddr = &ipv6_hdr(skb)->daddr;
|
||||||
|
@ -939,7 +943,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
|
||||||
|
|
||||||
type = hdr->icmp6_type;
|
type = hdr->icmp6_type;
|
||||||
|
|
||||||
ICMP6MSGIN_INC_STATS(dev_net(dev), idev, type);
|
ICMP6MSGIN_INC_STATS(dev_net_rcu(dev), idev, type);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ICMPV6_ECHO_REQUEST:
|
case ICMPV6_ECHO_REQUEST:
|
||||||
|
@ -1034,9 +1038,9 @@ static int icmpv6_rcv(struct sk_buff *skb)
|
||||||
|
|
||||||
csum_error:
|
csum_error:
|
||||||
reason = SKB_DROP_REASON_ICMP_CSUM;
|
reason = SKB_DROP_REASON_ICMP_CSUM;
|
||||||
__ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_CSUMERRORS);
|
__ICMP6_INC_STATS(dev_net_rcu(dev), idev, ICMP6_MIB_CSUMERRORS);
|
||||||
discard_it:
|
discard_it:
|
||||||
__ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INERRORS);
|
__ICMP6_INC_STATS(dev_net_rcu(dev), idev, ICMP6_MIB_INERRORS);
|
||||||
drop_no_count:
|
drop_no_count:
|
||||||
kfree_skb_reason(skb, reason);
|
kfree_skb_reason(skb, reason);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -477,9 +477,7 @@ discard:
|
||||||
static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
|
static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
skb_clear_delivery_time(skb);
|
skb_clear_delivery_time(skb);
|
||||||
rcu_read_lock();
|
|
||||||
ip6_protocol_deliver_rcu(net, skb, 0, false);
|
ip6_protocol_deliver_rcu(net, skb, 0, false);
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -487,9 +485,15 @@ static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *sk
|
||||||
|
|
||||||
int ip6_input(struct sk_buff *skb)
|
int ip6_input(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN,
|
int res;
|
||||||
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
|
|
||||||
ip6_input_finish);
|
rcu_read_lock();
|
||||||
|
res = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN,
|
||||||
|
dev_net_rcu(skb->dev), NULL, skb, skb->dev, NULL,
|
||||||
|
ip6_input_finish);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ip6_input);
|
EXPORT_SYMBOL_GPL(ip6_input);
|
||||||
|
|
||||||
|
|
|
@ -3196,13 +3196,18 @@ static unsigned int ip6_default_advmss(const struct dst_entry *dst)
|
||||||
{
|
{
|
||||||
struct net_device *dev = dst->dev;
|
struct net_device *dev = dst->dev;
|
||||||
unsigned int mtu = dst_mtu(dst);
|
unsigned int mtu = dst_mtu(dst);
|
||||||
struct net *net = dev_net(dev);
|
struct net *net;
|
||||||
|
|
||||||
mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
|
mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
net = dev_net_rcu(dev);
|
||||||
if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
|
if (mtu < net->ipv6.sysctl.ip6_rt_min_advmss)
|
||||||
mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
|
mtu = net->ipv6.sysctl.ip6_rt_min_advmss;
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
|
* Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and
|
||||||
* corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
|
* corresponding MSS is IPV6_MAXPLEN - tcp_header_size.
|
||||||
|
|
Loading…
Add table
Reference in a new issue