summaryrefslogtreecommitdiff
path: root/net/ipv6
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/addrconf.c30
-rw-r--r--net/ipv6/datagram.c12
-rw-r--r--net/ipv6/ip6_offload.c3
-rw-r--r--net/ipv6/netfilter/Kconfig12
-rw-r--r--net/ipv6/netfilter/ip6_tables.c30
-rw-r--r--net/ipv6/route.c6
-rw-r--r--net/ipv6/tcp_ipv6.c6
-rw-r--r--net/ipv6/udp.c67
8 files changed, 144 insertions, 22 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e73b3ebe5b9a..40c29712f32a 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -240,6 +240,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
},
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
+ .accept_ra_prefix_route = 1,
};
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -287,6 +288,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
},
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
+ .accept_ra_prefix_route = 1,
};
/* Check if a valid qdisc is available */
@@ -2081,6 +2083,16 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
return addrconf_ifid_ieee1394(eui, dev);
case ARPHRD_TUNNEL6:
return addrconf_ifid_ip6tnl(eui, dev);
+ case ARPHRD_RAWIP: {
+ struct in6_addr lladdr;
+
+ if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
+ get_random_bytes(eui, 8);
+ else
+ memcpy(eui, lladdr.s6_addr + 8, 8);
+
+ return 0;
+ }
}
return -1;
}
@@ -2463,8 +2475,11 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
flags |= RTF_EXPIRES;
expires = jiffies_to_clock_t(rt_expires);
}
- addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
- dev, expires, flags);
+ if (dev->ip6_ptr->cnf.accept_ra_prefix_route) {
+ addrconf_prefix_route(&pinfo->prefix,
+ pinfo->prefix_len,
+ dev, expires, flags);
+ }
}
ip6_rt_put(rt);
}
@@ -3128,7 +3143,9 @@ static void addrconf_dev_config(struct net_device *dev)
(dev->type != ARPHRD_IEEE802154) &&
(dev->type != ARPHRD_IEEE1394) &&
(dev->type != ARPHRD_TUNNEL6) &&
- (dev->type != ARPHRD_6LOWPAN)) {
+ (dev->type != ARPHRD_6LOWPAN) &&
+ (dev->type != ARPHRD_RAWIP) &&
+ (dev->type != ARPHRD_INFINIBAND)) {
/* Alas, we support only Ethernet autoconfiguration. */
return;
}
@@ -5810,6 +5827,13 @@ static struct addrconf_sysctl_table
.proc_handler = proc_dointvec,
},
{
+ .procname = "accept_ra_prefix_route",
+ .data = &ipv6_devconf.accept_ra_prefix_route,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "stable_secret",
.data = &ipv6_devconf.stable_secret,
.maxlen = IPV6_MAX_STRLEN,
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 56528e9f3e01..d7c1ee7cf0e2 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -184,6 +184,11 @@ ipv4_connected:
err = 0;
if (IS_ERR(dst)) {
err = PTR_ERR(dst);
+ /* Reset daddr and dport so that udp_v6_early_demux()
+ * fails to find this socket
+ */
+ memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr));
+ inet->inet_dport = 0;
goto out;
}
@@ -968,9 +973,14 @@ void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
__u16 srcp, __u16 destp, int bucket)
{
const struct in6_addr *dest, *src;
+ __u8 state = sp->sk_state;
dest = &sp->sk_v6_daddr;
src = &sp->sk_v6_rcv_saddr;
+
+ if (inet_sk(sp) && inet_sk(sp)->transparent)
+ state |= 0x80;
+
seq_printf(seq,
"%5d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
"%02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d\n",
@@ -979,7 +989,7 @@ void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
src->s6_addr32[2], src->s6_addr32[3], srcp,
dest->s6_addr32[0], dest->s6_addr32[1],
dest->s6_addr32[2], dest->s6_addr32[3], destp,
- sp->sk_state,
+ state,
sk_wmem_alloc_get(sp),
sk_rmem_alloc_get(sp),
0, 0L, 0,
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 9e2ea4ae840d..c612daad9e92 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -242,9 +242,6 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
/* flush if Traffic Class fields are different */
NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
NAPI_GRO_CB(p)->flush |= flush;
-
- /* Clear flush_id, there's really no concept of ID in IPv6. */
- NAPI_GRO_CB(p)->flush_id = 0;
}
NAPI_GRO_CB(skb)->flush |= flush;
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index e10a04c9cdc7..cf336d670f8b 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -135,6 +135,18 @@ config IP6_NF_IPTABLES
if IP6_NF_IPTABLES
+config IP6_NF_IPTABLES_128
+ tristate "128 bit arithmetic for iptables matching"
+ depends on IP6_NF_IPTABLES
+ help
+ This enables 128 bit matching in ip6tables to help optimize cases
+ where there is no match required. ip6tables matching for ipv6 always
+ has a mask if an address is specified for match. Adding a check for
+ mask prior to that helps to improve performance as it avoids the
+ masked comparison.
+
+ Note that this feature depends on the architecture. If unsure, say N.
+
# The simple matches.
config IP6_NF_MATCH_AH
tristate '"ah" match support'
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 22f39e00bef3..6fd784643d6e 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -94,22 +94,26 @@ ip6_packet_match(const struct sk_buff *skb,
{
unsigned long ret;
const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ const __uint128_t *ulm1 = (const __uint128_t *)&ip6info->smsk;
+ const __uint128_t *ulm2 = (const __uint128_t *)&ip6info->dmsk;
+#endif
#define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
- if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
- &ip6info->src), IP6T_INV_SRCIP) ||
- FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
- &ip6info->dst), IP6T_INV_DSTIP)) {
- dprintf("Source or dest mismatch.\n");
-/*
- dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
- ipinfo->smsk.s_addr, ipinfo->src.s_addr,
- ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
- dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
- ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
- ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
- return false;
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+ if (*ulm1 || *ulm2)
+#endif
+ {
+ if (FWINV(ipv6_masked_addr_cmp
+ (&ipv6->saddr, &ip6info->smsk, &ip6info->src),
+ IP6T_INV_SRCIP) ||
+ FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
+ &ip6info->dst),
+ IP6T_INV_DSTIP)) {
+ dprintf("Source or dest mismatch.\n");
+ return false;
+ }
}
ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index bf5b9551efb1..e367ce026db3 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3212,6 +3212,10 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
{
struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
int prefix;
+ struct net *net = arg->net;
+
+ if (rt == net->ipv6.ip6_null_entry)
+ return 0;
if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
@@ -3219,7 +3223,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
} else
prefix = 0;
- return rt6_fill_node(arg->net,
+ return rt6_fill_node(net,
arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
NETLINK_CB(arg->cb->skb).portid, arg->cb->nlh->nlmsg_seq,
prefix, 0, NLM_F_MULTI);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 7e29a31dd4f0..2bb5a6dc35e6 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1723,6 +1723,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
const struct fastopen_queue *fastopenq = &icsk->icsk_accept_queue.fastopenq;
int rx_queue;
int state;
+ __u8 state_seq = sp->sk_state;
dest = &sp->sk_v6_daddr;
src = &sp->sk_v6_rcv_saddr;
@@ -1754,6 +1755,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
*/
rx_queue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
+ if (inet->transparent)
+ state_seq |= 0x80;
+
seq_printf(seq,
"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
"%02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %lu %lu %u %u %d\n",
@@ -1762,7 +1766,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
src->s6_addr32[2], src->s6_addr32[3], srcp,
dest->s6_addr32[0], dest->s6_addr32[1],
dest->s6_addr32[2], dest->s6_addr32[3], destp,
- state,
+ state_seq,
tp->write_seq - tp->snd_una,
rx_queue,
timer_active,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 3b0e22d20e83..329ae3ccfa35 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -45,6 +45,7 @@
#include <net/tcp_states.h>
#include <net/ip6_checksum.h>
#include <net/xfrm.h>
+#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
#include <net/busy_poll.h>
@@ -958,6 +959,71 @@ discard:
return 0;
}
+static struct sock *__udp6_lib_demux_lookup(struct net *net,
+ __be16 loc_port, const struct in6_addr *loc_addr,
+ __be16 rmt_port, const struct in6_addr *rmt_addr,
+ int dif)
+{
+ struct sock *sk;
+ struct hlist_nulls_node *hnode;
+ unsigned short hnum = ntohs(loc_port);
+ unsigned int hash2 = udp6_portaddr_hash(net, loc_addr, hnum);
+ unsigned int slot2 = hash2 & udp_table.mask;
+ struct udp_hslot *hslot2 = &udp_table.hash2[slot2];
+
+ const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum);
+
+ udp_portaddr_for_each_entry_rcu(sk, hnode, &hslot2->head) {
+ if (sk->sk_state == TCP_ESTABLISHED &&
+ INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif))
+ return sk;
+ /* Only check first socket in chain */
+ break;
+ }
+ return NULL;
+}
+
+static void udp_v6_early_demux(struct sk_buff *skb)
+{
+ struct net *net = dev_net(skb->dev);
+ const struct udphdr *uh;
+ struct sock *sk;
+ struct dst_entry *dst;
+ int dif = skb->dev->ifindex;
+
+ if (!pskb_may_pull(skb, skb_transport_offset(skb) +
+ sizeof(struct udphdr)))
+ return;
+
+ uh = udp_hdr(skb);
+
+ if (skb->pkt_type == PACKET_HOST)
+ sk = __udp6_lib_demux_lookup(net, uh->dest,
+ &ipv6_hdr(skb)->daddr,
+ uh->source, &ipv6_hdr(skb)->saddr,
+ dif);
+ else
+ return;
+
+ if (!sk || !atomic_inc_not_zero_hint(&sk->sk_refcnt, 2))
+ return;
+
+ skb->sk = sk;
+ skb->destructor = sock_efree;
+ dst = READ_ONCE(sk->sk_rx_dst);
+
+ if (dst)
+ dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
+ if (dst) {
+ if (dst->flags & DST_NOCACHE) {
+ if (likely(atomic_inc_not_zero(&dst->__refcnt)))
+ skb_dst_set(skb, dst);
+ } else {
+ skb_dst_set_noref(skb, dst);
+ }
+ }
+}
+
static __inline__ int udpv6_rcv(struct sk_buff *skb)
{
return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP);
@@ -1466,6 +1532,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
#endif
static const struct inet6_protocol udpv6_protocol = {
+ .early_demux = udp_v6_early_demux,
.handler = udpv6_rcv,
.err_handler = udpv6_err,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,