ipv4: per-datagram IP_TOS and IP_TTL via sendmsg()
Subsystems (4)
cover ipv4: per-datagram IP_TOS and IP_TTL via sendmsg()
There is no way to set the IP_TOS field on a per-packet basis in IPv4, while IPv6 has such a mechanism. Therefore one has to fall back to the setsockopt() in case of IPv4.
Using the existing per-socket option is not convenient particularly in the situations where multiple threads have to use the same socket data requiring per-thread TOS values. In fact this would involve calling setsockopt() before sendmsg() every time.
An old patch was submitted to the netdev mailing list to add support for setting TOS on a per-packet basis, but was never integrated into the kernel. The old patch can be found here:
http://marc.info/?l=linux-netdev&m=118388241201593
This patch contains the changes from the original patch, updated for the current kernel.
Francesco Fusco (2):
ipv4: IP_TOS and IP_TTL can be specified as ancillary data
ipv4: processing ancillary IP_TOS or IP_TTL
include/net/inet_sock.h | 3 +++ include/net/ip.h | 3 +++ net/ipv4/icmp.c | 11 ++++++----- net/ipv4/ip_output.c | 17 ++++++++++++++--- net/ipv4/ip_sockglue.c | 20 +++++++++++++++++++- net/ipv4/raw.c | 3 +++ net/ipv4/udp.c | 3 +++ 7 files changed, 51 insertions(+), 9 deletions(-)
Signature · 1 line
1.8.3.1
PATCH 1/2 ipv4: IP_TOS and IP_TTL can be specified as ancillary data
This patch enables the IP_TTL and IP_TOS values passed from userspace to be stored in the ipcm_cookie struct.
--- include/net/ip.h | 3 +++ net/ipv4/ip_sockglue.c | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-)
include/net/ip.h +3 -0
@@ -54,6 +54,9 @@ static inline unsigned int ip_hdrlen(const struct sk_buff *skb)struct ipcm_cookie {__be32 addr;int oif; +__s16 ttl; +__s16 tos; +__u32 priority;struct ip_options_rcu *opt;__u8 tx_flags; };
net/ipv4/ip_sockglue.c +19 -1
@@ -189,7 +189,7 @@ EXPORT_SYMBOL(ip_cmsg_recv);int ip_cmsg_send (struct net *net,struct msghdr *msg,struct ipcm_cookie *ipc) { -int err; +int err, val;struct cmsghdr *cmsg;for (cmsg =CMSG_FIRSTHDR (msg); cmsg; cmsg =CMSG_NXTHDR (msg, cmsg)) {
@@ -215,6 +215,24 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) ipc->addr = info->ipi_spec_dst.s_addr;break ; } +case IP_TTL : +if (cmsg->cmsg_len !=CMSG_LEN (sizeof (int ))) +return -EINVAL ; + val = *(int *)CMSG_DATA (cmsg); +if (val <1 || val >255 ) +return -EINVAL ; + ipc->ttl = val; +break ; +case IP_TOS : +if (cmsg->cmsg_len !=CMSG_LEN (sizeof (int ))) +return -EINVAL ; + val = *(int *)CMSG_DATA (cmsg); +if (val <0 || val >255 ) +return -EINVAL ; + ipc->tos = val; + ipc->priority =rt_tos2priority (ipc->tos); +break ; +default :return -EINVAL ; }
Signature · 1 line
1.8.3.1
PATCH 2/2 ipv4: processing ancillary IP_TOS or IP_TTL
In reply to Francesco FuscoIf IP_TOS or IP_TTL are specified as ancillary data, then sendmsg() sends out packets with the specified TTL or TOS overriding the socket values specified with the traditional setsockopt().
If there is a per-packet specified tos, the skb->priority is set accordingly. The ipv4_is_multicast() function is used to fill in the right TTL value in case of multicast destinations.
--- include/net/inet_sock.h | 3 +++ net/ipv4/icmp.c | 11 ++++++----- net/ipv4/ip_output.c | 17 ++++++++++++++--- net/ipv4/raw.c | 3 +++ net/ipv4/udp.c | 3 +++ 5 files changed, 29 insertions(+), 8 deletions(-)
include/net/inet_sock.h +3 -0
@@ -101,6 +101,9 @@ struct inet_cork {struct ip_options *opt;unsigned int fragsize;int length;/* Total length of all frames */ +__s16 ttl; +__s16 tos; +__u32 priority;struct dst_entry *dst;u8 tx_flags; };
net/ipv4/icmp.c +6 -5
@@ -348,9 +348,11 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) icmp_param->data.icmph.checksum =0 ; - inet->tos =ip_hdr (skb)->tos; daddr = ipc.addr =ip_hdr (skb)->saddr; saddr =fib_compute_spec_dst (skb); + ipc.tos =ip_hdr (skb)->tos; + ipc.ttl =ipv4_is_multicast (daddr) ? inet->mc_ttl : inet->uc_ttl; + ipc.priority = sk->sk_priority; ipc.opt =NULL ; ipc.tx_flags =0 ;if (icmp_param->replyopts.opt.opt.optlen) {
@@ -487,7 +489,6 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)struct ipcm_cookie ipc;struct flowi4 fl4;__be32 saddr; -u8 tos;struct net *net;struct sock *sk;
@@ -586,7 +587,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)rcu_read_unlock (); } - tos = icmp_pointers[type].error ? ((iph->tos &IPTOS_TOS_MASK ) | + ipc.tos = icmp_pointers[type].error ? ((iph->tos &IPTOS_TOS_MASK ) |IPTOS_PREC_INTERNETCONTROL ) : iph->tos;
@@ -604,12 +605,12 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param->data.icmph.checksum =0 ; icmp_param->skb = skb_in; icmp_param->offset =skb_network_offset (skb_in); -inet_sk (sk)->tos = tos; + ipc.ttl =-1 ; ipc.addr = iph->saddr; ipc.opt = &icmp_param->replyopts.opt; ipc.tx_flags =0 ; - rt =icmp_route_lookup (net, &fl4, skb_in, iph, saddr, tos, + rt =icmp_route_lookup (net, &fl4, skb_in, iph, saddr, ipc.tos, type, code, icmp_param);if (IS_ERR (rt)) goto out_unlock;
net/ipv4/ip_output.c +14 -3
@@ -1068,6 +1068,9 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, rt->dst.dev->mtu : dst_mtu(&rt->dst); cork->dst = &rt->dst; cork->length =0 ; + cork->ttl = ipc->ttl; + cork->tos = ipc->tos; + cork->priority = ipc->priority; cork->tx_flags = ipc->tx_flags;return 0 ;
@@ -1319,7 +1322,9 @@ struct sk_buff *__ip_make_skb(struct sock *sk,if (cork->flags &IPCORK_OPT ) opt = cork->opt; -if (rt->rt_type ==RTN_MULTICAST ) +if (cork->ttl !=-1 ) + ttl = cork->ttl; +else if (rt->rt_type ==RTN_MULTICAST ) ttl = inet->mc_ttl;else ttl =ip_select_ttl (inet, &rt->dst);
@@ -1327,7 +1332,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, iph = (struct iphdr *)skb->data; iph->version =4 ; iph->ihl =5 ; - iph->tos = inet->tos; + iph->tos = (cork->tos != inet->tos) ? cork->tos : inet->tos; iph->frag_off = df; iph->ttl = ttl; iph->protocol = sk->sk_protocol;
@@ -1339,7 +1344,8 @@ struct sk_buff *__ip_make_skb(struct sock *sk,ip_options_build (skb, opt, cork->addr, rt,0 ); } - skb->priority = sk->sk_priority; + skb->priority = (cork->tos != inet->tos) ? + cork->priority : sk->sk_priority; skb->mark = sk->sk_mark;/* * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
@@ -1511,6 +1517,11 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, inet = &get_cpu_var (unicast_sock); inet->tos = arg->tos; + + ipc.tos =ip_hdr (skb)->tos; + ipc.ttl = inet->uc_ttl; + ipc.priority = skb->priority; + sk = &inet->sk; sk->sk_priority = skb->priority; sk->sk_protocol =ip_hdr (skb)->protocol;
net/ipv4/raw.c +3 -0
@@ -518,6 +518,9 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.opt =NULL ; ipc.tx_flags =0 ; ipc.oif = sk->sk_bound_dev_if; + ipc.ttl =ipv4_is_multicast (daddr) ? inet->mc_ttl : inet->uc_ttl; + ipc.tos = inet->tos; + ipc.priority = sk->sk_priority;if (msg->msg_controllen) { err =ip_cmsg_send (sock_net (sk), msg, &ipc);
net/ipv4/udp.c +3 -0
@@ -904,6 +904,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } ipc.addr = inet->inet_saddr; + ipc.ttl =ipv4_is_multicast (daddr) ? inet->mc_ttl : inet->uc_ttl; + ipc.tos = inet->tos; + ipc.priority = sk->sk_priority; ipc.oif = sk->sk_bound_dev_if;sock_tx_timestamp (sk, &ipc.tx_flags);
Signature · 1 line
1.8.3.1
David Miller
maintainerRe: [PATCH net-next 1/2] ipv4: IP_TOS and IP_TTL can be specified as ancillary data
In reply to Francesco FuscoFrom: Francesco Fusco <[email protected]>
Date: Wed, 14 Aug 2013 17:48:38 +0200
include/net/ip.h static inline unsigned int ip_hdrlen(const struct sk_buff *skb) · line 58
Quoted 2 lines
include/net/ip.h +2
-0+__s16 ttl; +__s16 tos;
You're range validating these values to make sure they are always between 1 and 255, inclusive.
Why use a 16-bit value, and in particular a signed one? That makes absolutely no sense at all.
These fit perfectly in a "u8" so use that instead.
Re: [PATCH net-next 2/2] ipv4: processing ancillary IP_TOS or IP_TTL
In reply to Francesco FuscoQuoted 12 lines · Francesco Fusco
If
IP_TOSorIP_TTLare specified as ancillary data, thensendmsg()sends out packets with the specified TTL or TOS overriding the socket values specified with the traditionalsetsockopt().If there is a per-packet specified tos, the
skb->priorityis set accordingly. Theipv4_is_multicast()function is used to fill in the right TTL value in case of multicast destinations.Signed-off-by Francesco Fusco <[email protected]>---
net/ipv4/ip_output.c struct sk_buff *__ip_make_skb(struct sock *sk, · line 1335
Quoted 5 lines
net/ipv4/ip_output.c +1
-1@@ -1327,7 +1332,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, iph = (struct iphdr *)skb->data; iph->version =4 ; iph->ihl =5 ; - iph->tos = inet->tos; + iph->tos = (cork->tos != inet->tos) ? cork->tos : inet->tos;
Strange construct, as the following has same meaning.
iph->tos = cork->tos;
Re: [PATCH net-next 2/2] ipv4: processing ancillary IP_TOS or IP_TTL
In reply to Francesco Fusconet/ipv4/ip_output.c void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, · line 1521
Quoted 6 lines · Francesco Fusco
net/ipv4/ip_output.c +2
-0@@ -1511,6 +1517,11 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, inet = &get_cpu_var (unicast_sock); inet->tos = arg->tos; + + ipc.tos =ip_hdr (skb)->tos;
Why both inet->tos and ipc.tos must be set ?
This is very confusing, as if you were not 100% sure of your patch.
net/ipv4/ip_output.c void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, · line 1522
Quoted 1 line
net/ipv4/ip_output.c +1
-0+ ipc.ttl = inet->uc_ttl;
ipc.ttl = -1;
net/ipv4/ip_output.c void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, · line 1526
Quoted 4 lines
net/ipv4/ip_output.c +2
-0+ ipc.priority = skb->priority; + sk = &inet->sk; sk->sk_priority = skb->priority;
Why both sk->sk_priority and ipc.priority must be set ?
Quoted 1 line
net/ipv4/ip_output.c +0
-0sk->sk_protocol =ip_hdr (skb)->protocol;