Commit b7394d24 authored by Cong Wang's avatar Cong Wang Committed by David S. Miller

netpoll: prepare for ipv6

This patch adjusts some struct and functions, to prepare
for supporting IPv6.

Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: default avatarCong Wang <amwang@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0c7768a0
......@@ -269,11 +269,13 @@ static ssize_t show_remote_port(struct netconsole_target *nt, char *buf)
static ssize_t show_local_ip(struct netconsole_target *nt, char *buf)
{
if (!nt->np.ipv6)
return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.local_ip);
}
static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf)
{
if (!nt->np.ipv6)
return snprintf(buf, PAGE_SIZE, "%pI4\n", &nt->np.remote_ip);
}
......@@ -410,7 +412,8 @@ static ssize_t store_local_ip(struct netconsole_target *nt,
return -EINVAL;
}
nt->np.local_ip = in_aton(buf);
if (!strnchr(buf, count, ':'))
nt->np.local_ip.ip = in_aton(buf);
return strnlen(buf, count);
}
......@@ -426,7 +429,8 @@ static ssize_t store_remote_ip(struct netconsole_target *nt,
return -EINVAL;
}
nt->np.remote_ip = in_aton(buf);
if (!strnchr(buf, count, ':'))
nt->np.remote_ip.ip = in_aton(buf);
return strnlen(buf, count);
}
......
......@@ -12,13 +12,22 @@
#include <linux/rcupdate.h>
#include <linux/list.h>
union inet_addr {
__u32 all[4];
__be32 ip;
__be32 ip6[4];
struct in_addr in;
struct in6_addr in6;
};
struct netpoll {
struct net_device *dev;
char dev_name[IFNAMSIZ];
const char *name;
void (*rx_hook)(struct netpoll *, int, char *, int);
__be32 local_ip, remote_ip;
union inet_addr local_ip, remote_ip;
bool ipv6;
u16 local_port, remote_port;
u8 remote_mac[ETH_ALEN];
......@@ -33,7 +42,7 @@ struct netpoll_info {
spinlock_t rx_lock;
struct list_head rx_np; /* netpolls that registered an rx_hook */
struct sk_buff_head arp_tx; /* list of arp requests to reply to */
struct sk_buff_head neigh_tx; /* list of neigh requests to reply to */
struct sk_buff_head txq;
struct delayed_work tx_work;
......
......@@ -55,7 +55,7 @@ static atomic_t trapped;
MAX_UDP_CHUNK)
static void zap_completion_queue(void);
static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo);
static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo);
static unsigned int carrier_timeout = 4;
module_param(carrier_timeout, uint, 0644);
......@@ -181,13 +181,13 @@ static void poll_napi(struct net_device *dev)
}
}
static void service_arp_queue(struct netpoll_info *npi)
static void service_neigh_queue(struct netpoll_info *npi)
{
if (npi) {
struct sk_buff *skb;
while ((skb = skb_dequeue(&npi->arp_tx)))
netpoll_arp_reply(skb, npi);
while ((skb = skb_dequeue(&npi->neigh_tx)))
netpoll_neigh_reply(skb, npi);
}
}
......@@ -216,14 +216,14 @@ static void netpoll_poll_dev(struct net_device *dev)
bond_dev = netdev_master_upper_dev_get_rcu(dev);
bond_ni = rcu_dereference_bh(bond_dev->npinfo);
while ((skb = skb_dequeue(&ni->arp_tx))) {
while ((skb = skb_dequeue(&ni->neigh_tx))) {
skb->dev = bond_dev;
skb_queue_tail(&bond_ni->arp_tx, skb);
skb_queue_tail(&bond_ni->neigh_tx, skb);
}
}
}
service_arp_queue(ni);
service_neigh_queue(ni);
zap_completion_queue();
}
......@@ -386,7 +386,9 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
static atomic_t ip_ident;
udp_len = len + sizeof(*udph);
if (!np->ipv6)
ip_len = udp_len + sizeof(*iph);
total_len = ip_len + LL_RESERVED_SPACE(np->dev);
skb = find_skb(np, total_len + np->dev->needed_tailroom,
......@@ -403,9 +405,11 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
udph->source = htons(np->local_port);
udph->dest = htons(np->remote_port);
udph->len = htons(udp_len);
if (!np->ipv6) {
udph->check = 0;
udph->check = csum_tcpudp_magic(np->local_ip,
np->remote_ip,
udph->check = csum_tcpudp_magic(np->local_ip.ip,
np->remote_ip.ip,
udp_len, IPPROTO_UDP,
csum_partial(udph, udp_len, 0));
if (udph->check == 0)
......@@ -424,13 +428,15 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
iph->ttl = 64;
iph->protocol = IPPROTO_UDP;
iph->check = 0;
put_unaligned(np->local_ip, &(iph->saddr));
put_unaligned(np->remote_ip, &(iph->daddr));
put_unaligned(np->local_ip.ip, &(iph->saddr));
put_unaligned(np->remote_ip.ip, &(iph->daddr));
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
skb->protocol = eth->h_proto = htons(ETH_P_IP);
}
memcpy(eth->h_source, np->dev->dev_addr, ETH_ALEN);
memcpy(eth->h_dest, np->remote_mac, ETH_ALEN);
......@@ -440,7 +446,7 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
}
EXPORT_SYMBOL(netpoll_send_udp);
static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
{
struct arphdr *arp;
unsigned char *arp_ptr;
......@@ -451,7 +457,7 @@ static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
struct netpoll *np, *tmp;
unsigned long flags;
int hlen, tlen;
int hits = 0;
int hits = 0, proto;
if (list_empty(&npinfo->rx_np))
return;
......@@ -469,6 +475,8 @@ static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
if (!hits)
return;
proto = ntohs(eth_hdr(skb)->h_proto);
if (proto == ETH_P_IP) {
/* No arp on this interface */
if (skb->dev->flags & IFF_NOARP)
return;
......@@ -505,7 +513,7 @@ static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
spin_lock_irqsave(&npinfo->rx_lock, flags);
list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) {
if (tip != np->local_ip)
if (tip != np->local_ip.ip)
continue;
hlen = LL_RESERVED_SPACE(np->dev);
......@@ -557,6 +565,7 @@ static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
break;
}
spin_unlock_irqrestore(&npinfo->rx_lock, flags);
}
}
int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
......@@ -576,7 +585,7 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
/* check if netpoll clients need ARP */
if (skb->protocol == htons(ETH_P_ARP) &&
atomic_read(&trapped)) {
skb_queue_tail(&npinfo->arp_tx, skb);
skb_queue_tail(&npinfo->neigh_tx, skb);
return 1;
}
......@@ -587,13 +596,14 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
}
proto = ntohs(eth_hdr(skb)->h_proto);
if (proto != ETH_P_IP)
if (proto != ETH_P_IP && proto != ETH_P_IPV6)
goto out;
if (skb->pkt_type == PACKET_OTHERHOST)
goto out;
if (skb_shared(skb))
goto out;
if (proto == ETH_P_IP) {
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto out;
iph = (struct iphdr *)skb->data;
......@@ -628,11 +638,10 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
goto out;
if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr))
goto out;
list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) {
if (np->local_ip && np->local_ip != iph->daddr)
if (np->local_ip.ip && np->local_ip.ip != iph->daddr)
continue;
if (np->remote_ip && np->remote_ip != iph->saddr)
if (np->remote_ip.ip && np->remote_ip.ip != iph->saddr)
continue;
if (np->local_port && np->local_port != ntohs(uh->dest))
continue;
......@@ -642,6 +651,7 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
ulen - sizeof(struct udphdr));
hits++;
}
}
if (!hits)
goto out;
......@@ -661,17 +671,40 @@ int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
void netpoll_print_options(struct netpoll *np)
{
np_info(np, "local port %d\n", np->local_port);
np_info(np, "local IP %pI4\n", &np->local_ip);
if (!np->ipv6)
np_info(np, "local IPv4 address %pI4\n", &np->local_ip.ip);
np_info(np, "interface '%s'\n", np->dev_name);
np_info(np, "remote port %d\n", np->remote_port);
np_info(np, "remote IP %pI4\n", &np->remote_ip);
if (!np->ipv6)
np_info(np, "remote IPv4 address %pI4\n", &np->remote_ip.ip);
np_info(np, "remote ethernet address %pM\n", np->remote_mac);
}
EXPORT_SYMBOL(netpoll_print_options);
static int netpoll_parse_ip_addr(const char *str, union inet_addr *addr)
{
const char *end;
if (!strchr(str, ':') &&
in4_pton(str, -1, (void *)addr, -1, &end) > 0) {
if (!*end)
return 0;
}
if (in6_pton(str, -1, addr->in6.s6_addr, -1, &end) > 0) {
#if IS_ENABLED(CONFIG_IPV6)
if (!*end)
return 1;
#else
return -1;
#endif
}
return -1;
}
int netpoll_parse_options(struct netpoll *np, char *opt)
{
char *cur=opt, *delim;
int ipv6;
if (*cur != '@') {
if ((delim = strchr(cur, '@')) == NULL)
......@@ -687,7 +720,11 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
if ((delim = strchr(cur, '/')) == NULL)
goto parse_failed;
*delim = 0;
np->local_ip = in_aton(cur);
ipv6 = netpoll_parse_ip_addr(cur, &np->local_ip);
if (ipv6 < 0)
goto parse_failed;
else
np->ipv6 = (bool)ipv6;
cur = delim;
}
cur++;
......@@ -719,7 +756,13 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
if ((delim = strchr(cur, '/')) == NULL)
goto parse_failed;
*delim = 0;
np->remote_ip = in_aton(cur);
ipv6 = netpoll_parse_ip_addr(cur, &np->remote_ip);
if (ipv6 < 0)
goto parse_failed;
else if (np->ipv6 != (bool)ipv6)
goto parse_failed;
else
np->ipv6 = (bool)ipv6;
cur = delim + 1;
if (*cur != 0) {
......@@ -767,7 +810,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
INIT_LIST_HEAD(&npinfo->rx_np);
spin_lock_init(&npinfo->rx_lock);
skb_queue_head_init(&npinfo->arp_tx);
skb_queue_head_init(&npinfo->neigh_tx);
skb_queue_head_init(&npinfo->txq);
INIT_DELAYED_WORK(&npinfo->tx_work, queue_process);
......@@ -859,10 +902,12 @@ int netpoll_setup(struct netpoll *np)
}
}
if (!np->local_ip) {
if (!np->local_ip.ip) {
if (!np->ipv6) {
rcu_read_lock();
in_dev = __in_dev_get_rcu(ndev);
if (!in_dev || !in_dev->ifa_list) {
rcu_read_unlock();
np_err(np, "no IP address for %s, aborting\n",
......@@ -871,9 +916,10 @@ int netpoll_setup(struct netpoll *np)
goto put;
}
np->local_ip = in_dev->ifa_list->ifa_local;
np->local_ip.ip = in_dev->ifa_list->ifa_local;
rcu_read_unlock();
np_info(np, "local IP %pI4\n", &np->local_ip);
np_info(np, "local IP %pI4\n", &np->local_ip.ip);
}
}
/* fill up the skb queue */
......@@ -906,7 +952,7 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head)
struct netpoll_info *npinfo =
container_of(rcu_head, struct netpoll_info, rcu);
skb_queue_purge(&npinfo->arp_tx);
skb_queue_purge(&npinfo->neigh_tx);
skb_queue_purge(&npinfo->txq);
/* we can't call cancel_delayed_work_sync here, as we are in softirq */
......
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