Skip to content

Commit

Permalink
net: ipv6: Add IPv6 support to the ping socket.
Browse files Browse the repository at this point in the history
This adds the ability to send ICMPv6 echo requests without a
raw socket. The equivalent ability for ICMPv4 was added in
2011.

Instead of having separate code paths for IPv4 and IPv6, make
most of the code in net/ipv4/ping.c dual-stack and only add a
few IPv6-specific bits (like the protocol definition) to a new
net/ipv6/ping.c. Hopefully this will reduce divergence and/or
duplication of bugs in the future.

Caveats:

- Setting options via ancillary data (e.g., using IPV6_PKTINFO
  to specify the outgoing interface) is not yet supported.
- There are no separate security settings for IPv4 and IPv6;
  everything is controlled by /proc/net/ipv4/ping_group_range.
- The proc interface does not yet display IPv6 ping sockets
  properly.

Tested with a patched copy of ping6 and using raw socket calls.
Compiles and works with all of CONFIG_IPV6={n,m,y}.

Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
lcolitti authored and davem330 committed May 26, 2013
1 parent 6e2842f commit 6d0bfe2
Show file tree
Hide file tree
Showing 9 changed files with 698 additions and 171 deletions.
6 changes: 6 additions & 0 deletions include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl)

extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info);

int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
struct icmp6hdr *thdr, int len);

struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
struct sock *sk, struct flowi6 *fl6);

extern int ip6_ra_control(struct sock *sk, int sel);

extern int ipv6_parse_hopopts(struct sk_buff *skb);
Expand Down
49 changes: 46 additions & 3 deletions include/net/ping.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef _PING_H
#define _PING_H

#include <net/icmp.h>
#include <net/netns/hash.h>

/* PING_HTABLE_SIZE must be power of 2 */
Expand All @@ -28,6 +29,18 @@
*/
#define GID_T_MAX (((gid_t)~0U) >> 1)

/* Compatibility glue so we can support IPv6 when it's compiled as a module */
struct pingv6_ops {
int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len);
int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg,
struct sk_buff *skb);
int (*icmpv6_err_convert)(u8 type, u8 code, int *err);
void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err,
__be16 port, u32 info, u8 *payload);
int (*ipv6_chk_addr)(struct net *net, const struct in6_addr *addr,
struct net_device *dev, int strict);
};

struct ping_table {
struct hlist_nulls_head hash[PING_HTABLE_SIZE];
rwlock_t lock;
Expand All @@ -39,17 +52,47 @@ struct ping_iter_state {
};

extern struct proto ping_prot;
extern struct ping_table ping_table;
#if IS_ENABLED(CONFIG_IPV6)
extern struct pingv6_ops pingv6_ops;
#endif

struct pingfakehdr {
struct icmphdr icmph;
struct iovec *iov;
sa_family_t family;
__wsum wcheck;
};

extern void ping_rcv(struct sk_buff *);
extern void ping_err(struct sk_buff *, u32 info);
int ping_get_port(struct sock *sk, unsigned short ident);
void ping_hash(struct sock *sk);
void ping_unhash(struct sock *sk);

int ping_init_sock(struct sock *sk);
void ping_close(struct sock *sk, long timeout);
int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len);
void ping_err(struct sk_buff *skb, int offset, u32 info);
int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd,
struct sk_buff *);

int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int noblock, int flags, int *addr_len);
int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
void *user_icmph, size_t icmph_len);
int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len);
int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len);
int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
void ping_rcv(struct sk_buff *skb);

#ifdef CONFIG_PROC_FS
extern int __init ping_proc_init(void);
extern void ping_proc_exit(void);
#endif

void __init ping_init(void);

int __init pingv6_init(void);
void pingv6_exit(void);

#endif /* _PING_H */
3 changes: 3 additions & 0 deletions include/net/transp_v6.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern struct proto rawv6_prot;
extern struct proto udpv6_prot;
extern struct proto udplitev6_prot;
extern struct proto tcpv6_prot;
extern struct proto pingv6_prot;

struct flowi6;

Expand All @@ -21,6 +22,8 @@ extern int ipv6_frag_init(void);
extern void ipv6_frag_exit(void);

/* transport protocols */
extern int pingv6_init(void);
extern void pingv6_exit(void);
extern int rawv6_init(void);
extern void rawv6_exit(void);
extern int udpv6_init(void);
Expand Down
5 changes: 3 additions & 2 deletions net/ipv4/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,8 @@ int icmp_rcv(struct sk_buff *skb)
void icmp_err(struct sk_buff *skb, u32 info)
{
struct iphdr *iph = (struct iphdr *)skb->data;
struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2));
int offset = iph->ihl<<2;
struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
int type = icmp_hdr(skb)->type;
int code = icmp_hdr(skb)->code;
struct net *net = dev_net(skb->dev);
Expand All @@ -949,7 +950,7 @@ void icmp_err(struct sk_buff *skb, u32 info)
* triggered by ICMP_ECHOREPLY which sent from kernel.
*/
if (icmph->type != ICMP_ECHOREPLY) {
ping_err(skb, info);
ping_err(skb, offset, info);
return;
}

Expand Down
Loading

0 comments on commit 6d0bfe2

Please sign in to comment.