Commit 73ce00d4 authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
The following patchset contains Netfilter/IPVS fixes for 3.10-rc3,
they are:

* fix xt_addrtype with IPv6, from Florian Westphal. This required
  a new hook for IPv6 functions in the netfilter core to avoid
  hard dependencies with the ipv6 subsystem when this match is
  only used for IPv4.

* fix connection reuse case in IPVS. Currently, if an reused
  connection are directed to the same server. If that server is
  down, those connection would fail. Therefore, clear the
  connection and choose a new server among the available ones.

* fix possible non-nul terminated string sent to user-space if
  ipt_ULOG is used as the default netfilter logging stub, from
  Chen Gang.

* fix mark logging of IPv6 packets in xt_LOG, from Michal Kubecek.
  This bug has been there since 2.6.26.

* Fix breakage ip_vs_sh due to incorrect structure layout for
  RCU, from Jan Beulich.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8e6d91ae a70b9641
...@@ -17,6 +17,22 @@ extern __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, ...@@ -17,6 +17,22 @@ extern __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
extern int ipv6_netfilter_init(void); extern int ipv6_netfilter_init(void);
extern void ipv6_netfilter_fini(void); extern void ipv6_netfilter_fini(void);
/*
* Hook functions for ipv6 to allow xt_* modules to be built-in even
* if IPv6 is a module.
*/
struct nf_ipv6_ops {
int (*chk_addr)(struct net *net, const struct in6_addr *addr,
const struct net_device *dev, int strict);
};
extern const struct nf_ipv6_ops __rcu *nf_ipv6_ops;
static inline const struct nf_ipv6_ops *nf_get_ipv6_ops(void)
{
return rcu_dereference(nf_ipv6_ops);
}
#else /* CONFIG_NETFILTER */ #else /* CONFIG_NETFILTER */
static inline int ipv6_netfilter_init(void) { return 0; } static inline int ipv6_netfilter_init(void) { return 0; }
static inline void ipv6_netfilter_fini(void) { return; } static inline void ipv6_netfilter_fini(void) { return; }
......
...@@ -65,7 +65,7 @@ extern int addrconf_set_dstaddr(struct net *net, ...@@ -65,7 +65,7 @@ extern int addrconf_set_dstaddr(struct net *net,
extern int ipv6_chk_addr(struct net *net, extern int ipv6_chk_addr(struct net *net,
const struct in6_addr *addr, const struct in6_addr *addr,
struct net_device *dev, const struct net_device *dev,
int strict); int strict);
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
......
...@@ -231,8 +231,10 @@ static void ipt_ulog_packet(struct net *net, ...@@ -231,8 +231,10 @@ static void ipt_ulog_packet(struct net *net,
put_unaligned(tv.tv_usec, &pm->timestamp_usec); put_unaligned(tv.tv_usec, &pm->timestamp_usec);
put_unaligned(skb->mark, &pm->mark); put_unaligned(skb->mark, &pm->mark);
pm->hook = hooknum; pm->hook = hooknum;
if (prefix != NULL) if (prefix != NULL) {
strncpy(pm->prefix, prefix, sizeof(pm->prefix)); strncpy(pm->prefix, prefix, sizeof(pm->prefix) - 1);
pm->prefix[sizeof(pm->prefix) - 1] = '\0';
}
else if (loginfo->prefix[0] != '\0') else if (loginfo->prefix[0] != '\0')
strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix)); strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix));
else else
......
...@@ -1487,7 +1487,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev) ...@@ -1487,7 +1487,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev)
} }
int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
struct net_device *dev, int strict) const struct net_device *dev, int strict)
{ {
struct inet6_ifaddr *ifp; struct inet6_ifaddr *ifp;
unsigned int hash = inet6_addr_hash(addr); unsigned int hash = inet6_addr_hash(addr);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
#include <linux/export.h> #include <linux/export.h>
#include <net/addrconf.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/ip6_route.h> #include <net/ip6_route.h>
...@@ -186,6 +187,10 @@ static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook, ...@@ -186,6 +187,10 @@ static __sum16 nf_ip6_checksum_partial(struct sk_buff *skb, unsigned int hook,
return csum; return csum;
}; };
static const struct nf_ipv6_ops ipv6ops = {
.chk_addr = ipv6_chk_addr,
};
static const struct nf_afinfo nf_ip6_afinfo = { static const struct nf_afinfo nf_ip6_afinfo = {
.family = AF_INET6, .family = AF_INET6,
.checksum = nf_ip6_checksum, .checksum = nf_ip6_checksum,
...@@ -198,6 +203,7 @@ static const struct nf_afinfo nf_ip6_afinfo = { ...@@ -198,6 +203,7 @@ static const struct nf_afinfo nf_ip6_afinfo = {
int __init ipv6_netfilter_init(void) int __init ipv6_netfilter_init(void)
{ {
RCU_INIT_POINTER(nf_ipv6_ops, &ipv6ops);
return nf_register_afinfo(&nf_ip6_afinfo); return nf_register_afinfo(&nf_ip6_afinfo);
} }
...@@ -206,5 +212,6 @@ int __init ipv6_netfilter_init(void) ...@@ -206,5 +212,6 @@ int __init ipv6_netfilter_init(void)
*/ */
void ipv6_netfilter_fini(void) void ipv6_netfilter_fini(void)
{ {
RCU_INIT_POINTER(nf_ipv6_ops, NULL);
nf_unregister_afinfo(&nf_ip6_afinfo); nf_unregister_afinfo(&nf_ip6_afinfo);
} }
...@@ -30,6 +30,8 @@ static DEFINE_MUTEX(afinfo_mutex); ...@@ -30,6 +30,8 @@ static DEFINE_MUTEX(afinfo_mutex);
const struct nf_afinfo __rcu *nf_afinfo[NFPROTO_NUMPROTO] __read_mostly; const struct nf_afinfo __rcu *nf_afinfo[NFPROTO_NUMPROTO] __read_mostly;
EXPORT_SYMBOL(nf_afinfo); EXPORT_SYMBOL(nf_afinfo);
const struct nf_ipv6_ops __rcu *nf_ipv6_ops __read_mostly;
EXPORT_SYMBOL_GPL(nf_ipv6_ops);
int nf_register_afinfo(const struct nf_afinfo *afinfo) int nf_register_afinfo(const struct nf_afinfo *afinfo)
{ {
......
...@@ -1001,6 +1001,32 @@ static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len) ...@@ -1001,6 +1001,32 @@ static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len)
return th->rst; return th->rst;
} }
static inline bool is_new_conn(const struct sk_buff *skb,
struct ip_vs_iphdr *iph)
{
switch (iph->protocol) {
case IPPROTO_TCP: {
struct tcphdr _tcph, *th;
th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
if (th == NULL)
return false;
return th->syn;
}
case IPPROTO_SCTP: {
sctp_chunkhdr_t *sch, schunk;
sch = skb_header_pointer(skb, iph->len + sizeof(sctp_sctphdr_t),
sizeof(schunk), &schunk);
if (sch == NULL)
return false;
return sch->type == SCTP_CID_INIT;
}
default:
return false;
}
}
/* Handle response packets: rewrite addresses and send away... /* Handle response packets: rewrite addresses and send away...
*/ */
static unsigned int static unsigned int
...@@ -1612,6 +1638,15 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1612,6 +1638,15 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
* Check if the packet belongs to an existing connection entry * Check if the packet belongs to an existing connection entry
*/ */
cp = pp->conn_in_get(af, skb, &iph, 0); cp = pp->conn_in_get(af, skb, &iph, 0);
if (unlikely(sysctl_expire_nodest_conn(ipvs)) && cp && cp->dest &&
unlikely(!atomic_read(&cp->dest->weight)) && !iph.fragoffs &&
is_new_conn(skb, &iph)) {
ip_vs_conn_expire_now(cp);
__ip_vs_conn_put(cp);
cp = NULL;
}
if (unlikely(!cp) && !iph.fragoffs) { if (unlikely(!cp) && !iph.fragoffs) {
/* No (second) fragments need to enter here, as nf_defrag_ipv6 /* No (second) fragments need to enter here, as nf_defrag_ipv6
* replayed fragment zero will already have created the cp * replayed fragment zero will already have created the cp
......
...@@ -67,8 +67,8 @@ struct ip_vs_sh_bucket { ...@@ -67,8 +67,8 @@ struct ip_vs_sh_bucket {
#define IP_VS_SH_TAB_MASK (IP_VS_SH_TAB_SIZE - 1) #define IP_VS_SH_TAB_MASK (IP_VS_SH_TAB_SIZE - 1)
struct ip_vs_sh_state { struct ip_vs_sh_state {
struct ip_vs_sh_bucket buckets[IP_VS_SH_TAB_SIZE];
struct rcu_head rcu_head; struct rcu_head rcu_head;
struct ip_vs_sh_bucket buckets[IP_VS_SH_TAB_SIZE];
}; };
/* /*
......
...@@ -737,7 +737,7 @@ static void dump_ipv6_packet(struct sbuff *m, ...@@ -737,7 +737,7 @@ static void dump_ipv6_packet(struct sbuff *m,
dump_sk_uid_gid(m, skb->sk); dump_sk_uid_gid(m, skb->sk);
/* Max length: 16 "MARK=0xFFFFFFFF " */ /* Max length: 16 "MARK=0xFFFFFFFF " */
if (!recurse && skb->mark) if (recurse && skb->mark)
sb_add(m, "MARK=0x%x ", skb->mark); sb_add(m, "MARK=0x%x ", skb->mark);
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <net/ip6_fib.h> #include <net/ip6_fib.h>
#endif #endif
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/xt_addrtype.h> #include <linux/netfilter/xt_addrtype.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
...@@ -33,12 +34,12 @@ MODULE_ALIAS("ip6t_addrtype"); ...@@ -33,12 +34,12 @@ MODULE_ALIAS("ip6t_addrtype");
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
static u32 match_lookup_rt6(struct net *net, const struct net_device *dev, static u32 match_lookup_rt6(struct net *net, const struct net_device *dev,
const struct in6_addr *addr) const struct in6_addr *addr, u16 mask)
{ {
const struct nf_afinfo *afinfo; const struct nf_afinfo *afinfo;
struct flowi6 flow; struct flowi6 flow;
struct rt6_info *rt; struct rt6_info *rt;
u32 ret; u32 ret = 0;
int route_err; int route_err;
memset(&flow, 0, sizeof(flow)); memset(&flow, 0, sizeof(flow));
...@@ -49,12 +50,19 @@ static u32 match_lookup_rt6(struct net *net, const struct net_device *dev, ...@@ -49,12 +50,19 @@ static u32 match_lookup_rt6(struct net *net, const struct net_device *dev,
rcu_read_lock(); rcu_read_lock();
afinfo = nf_get_afinfo(NFPROTO_IPV6); afinfo = nf_get_afinfo(NFPROTO_IPV6);
if (afinfo != NULL) if (afinfo != NULL) {
const struct nf_ipv6_ops *v6ops;
if (dev && (mask & XT_ADDRTYPE_LOCAL)) {
v6ops = nf_get_ipv6_ops();
if (v6ops && v6ops->chk_addr(net, addr, dev, true))
ret = XT_ADDRTYPE_LOCAL;
}
route_err = afinfo->route(net, (struct dst_entry **)&rt, route_err = afinfo->route(net, (struct dst_entry **)&rt,
flowi6_to_flowi(&flow), !!dev); flowi6_to_flowi(&flow), false);
else } else {
route_err = 1; route_err = 1;
}
rcu_read_unlock(); rcu_read_unlock();
if (route_err) if (route_err)
...@@ -62,15 +70,12 @@ static u32 match_lookup_rt6(struct net *net, const struct net_device *dev, ...@@ -62,15 +70,12 @@ static u32 match_lookup_rt6(struct net *net, const struct net_device *dev,
if (rt->rt6i_flags & RTF_REJECT) if (rt->rt6i_flags & RTF_REJECT)
ret = XT_ADDRTYPE_UNREACHABLE; ret = XT_ADDRTYPE_UNREACHABLE;
else
ret = 0;
if (rt->rt6i_flags & RTF_LOCAL) if (dev == NULL && rt->rt6i_flags & RTF_LOCAL)
ret |= XT_ADDRTYPE_LOCAL; ret |= XT_ADDRTYPE_LOCAL;
if (rt->rt6i_flags & RTF_ANYCAST) if (rt->rt6i_flags & RTF_ANYCAST)
ret |= XT_ADDRTYPE_ANYCAST; ret |= XT_ADDRTYPE_ANYCAST;
dst_release(&rt->dst); dst_release(&rt->dst);
return ret; return ret;
} }
...@@ -90,7 +95,7 @@ static bool match_type6(struct net *net, const struct net_device *dev, ...@@ -90,7 +95,7 @@ static bool match_type6(struct net *net, const struct net_device *dev,
if ((XT_ADDRTYPE_LOCAL | XT_ADDRTYPE_ANYCAST | if ((XT_ADDRTYPE_LOCAL | XT_ADDRTYPE_ANYCAST |
XT_ADDRTYPE_UNREACHABLE) & mask) XT_ADDRTYPE_UNREACHABLE) & mask)
return !!(mask & match_lookup_rt6(net, dev, addr)); return !!(mask & match_lookup_rt6(net, dev, addr, mask));
return true; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment