Skip to content

Commit

Permalink
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/ebiederm/net-next

Eric W. Biederman says:

====================
net: Pass net through ip fragmention

This is the next installment of my work to pass struct net through the
output path so the code does not need to guess how to figure out which
network namespace it is in, and ultimately routes can have output
devices in another network namespace.

This round focuses on passing net through ip fragmentation which we seem
to call from about everywhere.  That is the main ip output paths, the
bridge netfilter code, and openvswitch.  This has to happend at once
accross the tree as function pointers are involved.

First some prep work is done, then ipv4 and ipv6 are converted and then
temporary helper functions are removed.
====================

Acked-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 5, 2015
2 parents 7e2832f + b1842ff commit 40e1068
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 56 deletions.
4 changes: 2 additions & 2 deletions include/linux/netfilter_ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ struct nf_ipv6_ops {
int (*chk_addr)(struct net *net, const struct in6_addr *addr,
const struct net_device *dev, int strict);
void (*route_input)(struct sk_buff *skb);
int (*fragment)(struct sock *sk, struct sk_buff *skb,
int (*output)(struct sock *, struct sk_buff *));
int (*fragment)(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct net *, struct sock *, struct sk_buff *));
};

#ifdef CONFIG_NETFILTER
Expand Down
4 changes: 2 additions & 2 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ int ip_local_deliver(struct sk_buff *skb);
int ip_mr_input(struct sk_buff *skb);
int ip_output(struct sock *sk, struct sk_buff *skb);
int ip_mc_output(struct sock *sk, struct sk_buff *skb);
int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
int (*output)(struct sock *, struct sk_buff *));
int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct net *, struct sock *, struct sk_buff *));
void ip_send_check(struct iphdr *ip);
int __ip_local_out(struct sk_buff *skb);
int ip_local_out_sk(struct sock *sk, struct sk_buff *skb);
Expand Down
4 changes: 2 additions & 2 deletions include/net/ip6_route.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ static inline bool ipv6_anycast_destination(const struct dst_entry *dst,
ipv6_addr_equal(&rt->rt6i_dst.addr, daddr));
}

int ip6_fragment(struct sock *sk, struct sk_buff *skb,
int (*output)(struct sock *, struct sk_buff *));
int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct net *, struct sock *, struct sk_buff *));

static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
{
Expand Down
13 changes: 4 additions & 9 deletions net/bridge/br_netfilter_hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,17 +691,12 @@ static int br_nf_push_frag_xmit(struct net *net, struct sock *sk, struct sk_buff
nf_bridge_info_free(skb);
return br_dev_queue_push_xmit(net, sk, skb);
}
static int br_nf_push_frag_xmit_sk(struct sock *sk, struct sk_buff *skb)
{
struct net *net = dev_net(skb_dst(skb)->dev);
return br_nf_push_frag_xmit(net, sk, skb);
}
#endif

#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
static int
br_nf_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct sock *, struct sk_buff *))
int (*output)(struct net *, struct sock *, struct sk_buff *))
{
unsigned int mtu = ip_skb_dst_mtu(skb);
struct iphdr *iph = ip_hdr(skb);
Expand All @@ -714,7 +709,7 @@ br_nf_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
return -EMSGSIZE;
}

return ip_do_fragment(sk, skb, output);
return ip_do_fragment(net, sk, skb, output);
}
#endif

Expand Down Expand Up @@ -763,7 +758,7 @@ static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff
skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
data->size);

return br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit_sk);
return br_nf_ip_fragment(net, sk, skb, br_nf_push_frag_xmit);
}
#endif
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
Expand All @@ -786,7 +781,7 @@ static int br_nf_dev_queue_xmit(struct net *net, struct sock *sk, struct sk_buff
data->size);

if (v6ops)
return v6ops->fragment(sk, skb, br_nf_push_frag_xmit_sk);
return v6ops->fragment(net, sk, skb, br_nf_push_frag_xmit);

kfree_skb(skb);
return -EMSGSIZE;
Expand Down
44 changes: 20 additions & 24 deletions net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,10 @@
int sysctl_ip_default_ttl __read_mostly = IPDEFTTL;
EXPORT_SYMBOL(sysctl_ip_default_ttl);

static int ip_fragment(struct sock *sk, struct sk_buff *skb,
unsigned int mtu,
int (*output)(struct sock *, struct sk_buff *));
static int
ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
unsigned int mtu,
int (*output)(struct net *, struct sock *, struct sk_buff *));

/* Generate a checksum for an outgoing IP datagram. */
void ip_send_check(struct iphdr *iph)
Expand Down Expand Up @@ -176,12 +177,11 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk,
}
EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);

static int ip_finish_output2(struct sock *sk, struct sk_buff *skb)
static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct rtable *rt = (struct rtable *)dst;
struct net_device *dev = dst->dev;
struct net *net = dev_net(dev);
unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh;
u32 nexthop;
Expand Down Expand Up @@ -225,8 +225,8 @@ static int ip_finish_output2(struct sock *sk, struct sk_buff *skb)
return -EINVAL;
}

static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb,
unsigned int mtu)
static int ip_finish_output_gso(struct net *net, struct sock *sk,
struct sk_buff *skb, unsigned int mtu)
{
netdev_features_t features;
struct sk_buff *segs;
Expand All @@ -235,7 +235,7 @@ static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb,
/* common case: locally created skb or seglen is <= mtu */
if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) ||
skb_gso_network_seglen(skb) <= mtu)
return ip_finish_output2(sk, skb);
return ip_finish_output2(net, sk, skb);

/* Slowpath - GSO segment length is exceeding the dst MTU.
*
Expand All @@ -258,7 +258,7 @@ static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb,
int err;

segs->next = NULL;
err = ip_fragment(sk, segs, mtu, ip_finish_output2);
err = ip_fragment(net, sk, segs, mtu, ip_finish_output2);

if (err && ret == 0)
ret = err;
Expand All @@ -281,12 +281,12 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk
#endif
mtu = ip_skb_dst_mtu(skb);
if (skb_is_gso(skb))
return ip_finish_output_gso(sk, skb, mtu);
return ip_finish_output_gso(net, sk, skb, mtu);

if (skb->len > mtu || (IPCB(skb)->flags & IPSKB_FRAG_PMTU))
return ip_fragment(sk, skb, mtu, ip_finish_output2);
return ip_fragment(net, sk, skb, mtu, ip_finish_output2);

return ip_finish_output2(sk, skb);
return ip_finish_output2(net, sk, skb);
}

int ip_mc_output(struct sock *sk, struct sk_buff *skb)
Expand Down Expand Up @@ -495,28 +495,26 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
skb_copy_secmark(to, from);
}

static int ip_fragment(struct sock *sk, struct sk_buff *skb,
static int ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
unsigned int mtu,
int (*output)(struct sock *, struct sk_buff *))
int (*output)(struct net *, struct sock *, struct sk_buff *))
{
struct iphdr *iph = ip_hdr(skb);

if ((iph->frag_off & htons(IP_DF)) == 0)
return ip_do_fragment(sk, skb, output);
return ip_do_fragment(net, sk, skb, output);

if (unlikely(!skb->ignore_df ||
(IPCB(skb)->frag_max_size &&
IPCB(skb)->frag_max_size > mtu))) {
struct net *net = dev_net(skb_rtable(skb)->dst.dev);

IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(mtu));
kfree_skb(skb);
return -EMSGSIZE;
}

return ip_do_fragment(sk, skb, output);
return ip_do_fragment(net, sk, skb, output);
}

/*
Expand All @@ -526,8 +524,8 @@ static int ip_fragment(struct sock *sk, struct sk_buff *skb,
* single device frame, and queue such a frame for sending.
*/

int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
int (*output)(struct sock *, struct sk_buff *))
int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct net *, struct sock *, struct sk_buff *))
{
struct iphdr *iph;
int ptr;
Expand All @@ -537,11 +535,9 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
int offset;
__be16 not_last_frag;
struct rtable *rt = skb_rtable(skb);
struct net *net;
int err = 0;

dev = rt->dst.dev;
net = dev_net(dev);

/*
* Point into the IP datagram header.
Expand Down Expand Up @@ -631,7 +627,7 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,
ip_send_check(iph);
}

err = output(sk, skb);
err = output(net, sk, skb);

if (!err)
IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
Expand Down Expand Up @@ -771,7 +767,7 @@ int ip_do_fragment(struct sock *sk, struct sk_buff *skb,

ip_send_check(iph);

err = output(sk, skb2);
err = output(net, sk, skb2);
if (err)
goto fail;

Expand Down
16 changes: 7 additions & 9 deletions net/ipv6/ip6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,10 @@
#include <net/checksum.h>
#include <linux/mroute6.h>

static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct net_device *dev = dst->dev;
struct net *net = dev_net(dev);
struct neighbour *neigh;
struct in6_addr *nexthop;
int ret;
Expand Down Expand Up @@ -126,9 +125,9 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)) ||
(IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
return ip6_fragment(sk, skb, ip6_finish_output2);
return ip6_fragment(net, sk, skb, ip6_finish_output2);
else
return ip6_finish_output2(sk, skb);
return ip6_finish_output2(net, sk, skb);
}

int ip6_output(struct sock *sk, struct sk_buff *skb)
Expand Down Expand Up @@ -554,8 +553,8 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
skb_copy_secmark(to, from);
}

int ip6_fragment(struct sock *sk, struct sk_buff *skb,
int (*output)(struct sock *, struct sk_buff *))
int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct net *, struct sock *, struct sk_buff *))
{
struct sk_buff *frag;
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
Expand All @@ -568,7 +567,6 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
__be32 frag_id;
int ptr, offset = 0, err = 0;
u8 *prevhdr, nexthdr = 0;
struct net *net = dev_net(skb_dst(skb)->dev);

hlen = ip6_find_1stfragopt(skb, &prevhdr);
nexthdr = *prevhdr;
Expand Down Expand Up @@ -688,7 +686,7 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
ip6_copy_metadata(frag, skb);
}

err = output(sk, skb);
err = output(net, sk, skb);
if (!err)
IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
IPSTATS_MIB_FRAGCREATES);
Expand Down Expand Up @@ -816,7 +814,7 @@ int ip6_fragment(struct sock *sk, struct sk_buff *skb,
/*
* Put this fragment into the sending queue.
*/
err = output(sk, frag);
err = output(net, sk, frag);
if (err)
goto fail;

Expand Down
11 changes: 9 additions & 2 deletions net/ipv6/xfrm6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb)
return xfrm_output(sk, skb);
}

static int __xfrm6_output_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct xfrm_state *x = skb_dst(skb)->xfrm;

return x->outer_mode->afinfo->output_finish(sk, skb);
}

static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
Expand Down Expand Up @@ -160,8 +167,8 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
if (x->props.mode == XFRM_MODE_TUNNEL &&
((skb->len > mtu && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)))) {
return ip6_fragment(sk, skb,
x->outer_mode->afinfo->output_finish);
return ip6_fragment(net, sk, skb,
__xfrm6_output_finish);
}
return x->outer_mode->afinfo->output_finish(sk, skb);
}
Expand Down
13 changes: 7 additions & 6 deletions net/openvswitch/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ static int set_sctp(struct sk_buff *skb, struct sw_flow_key *flow_key,
return 0;
}

static int ovs_vport_output(struct sock *sock, struct sk_buff *skb)
static int ovs_vport_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct ovs_frag_data *data = this_cpu_ptr(&ovs_frag_data_storage);
struct vport *vport = data->vport;
Expand Down Expand Up @@ -679,8 +679,8 @@ static void prepare_frag(struct vport *vport, struct sk_buff *skb)
skb_pull(skb, hlen);
}

static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
__be16 ethertype)
static void ovs_fragment(struct net *net, struct vport *vport,
struct sk_buff *skb, u16 mru, __be16 ethertype)
{
if (skb_network_offset(skb) > MAX_L2_LEN) {
OVS_NLERR(1, "L2 header too long to fragment");
Expand All @@ -700,7 +700,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
skb_dst_set_noref(skb, &ovs_dst);
IPCB(skb)->frag_max_size = mru;

ip_do_fragment(skb->sk, skb, ovs_vport_output);
ip_do_fragment(net, skb->sk, skb, ovs_vport_output);
refdst_drop(orig_dst);
} else if (ethertype == htons(ETH_P_IPV6)) {
const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
Expand All @@ -722,7 +722,7 @@ static void ovs_fragment(struct vport *vport, struct sk_buff *skb, u16 mru,
skb_dst_set_noref(skb, &ovs_rt.dst);
IP6CB(skb)->frag_max_size = mru;

v6ops->fragment(skb->sk, skb, ovs_vport_output);
v6ops->fragment(net, skb->sk, skb, ovs_vport_output);
refdst_drop(orig_dst);
} else {
WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.",
Expand All @@ -743,6 +743,7 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
if (likely(!mru || (skb->len <= mru + ETH_HLEN))) {
ovs_vport_send(vport, skb);
} else if (mru <= vport->dev->mtu) {
struct net *net = read_pnet(&dp->net);
__be16 ethertype = key->eth.type;

if (!is_flow_key_valid(key)) {
Expand All @@ -752,7 +753,7 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
ethertype = vlan_get_protocol(skb);
}

ovs_fragment(vport, skb, mru, ethertype);
ovs_fragment(net, vport, skb, mru, ethertype);
} else {
kfree_skb(skb);
}
Expand Down

0 comments on commit 40e1068

Please sign in to comment.