aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/ip_output.c
diff options
context:
space:
mode:
authorHannes Frederic Sowa <hannes@stressinduktion.org>2013-10-27 12:29:11 -0400
committerDavid S. Miller <davem@davemloft.net>2013-10-29 00:15:22 -0400
commitdaba287b299ec7a2c61ae3a714920e90e8396ad5 (patch)
tree388feed8fdf3b531da0d365a06d36ae6fcd5bdab /net/ipv4/ip_output.c
parent2613af0ed18a11d5c566a81f9a6510b73180660a (diff)
ipv4: fix DO and PROBE pmtu mode regarding local fragmentation with UFO/CORK
UFO as well as UDP_CORK do not respect IP_PMTUDISC_DO and IP_PMTUDISC_PROBE well enough. UFO enabled packet delivery just appends all frags to the cork and hands it over to the network card. So we just deliver non-DF udp fragments (DF-flag may get overwritten by hardware or virtual UFO enabled interface). UDP_CORK does enqueue the data until the cork is disengaged. At this point it sets the correct IP_DF and local_df flags and hands it over to ip_fragment which in this case will generate an icmp error which gets appended to the error socket queue. This is not reflected in the syscall error (of course, if UFO is enabled this also won't happen). Improve this by checking the pmtudisc flags before appending data to the socket and if we still can fit all data in one packet when IP_PMTUDISC_DO or IP_PMTUDISC_PROBE is set, only then proceed. We use (mtu-fragheaderlen) to check for the maximum length because we ensure not to generate a fragment and non-fragmented data does not need to have its length aligned on 64 bit boundaries. Also the passed in ip_options are already aligned correctly. Maybe, we can relax some other checks around ip_fragment. This needs more research. Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r--net/ipv4/ip_output.c12
1 files changed, 8 insertions, 4 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 8fbac7de1e1b..51be64e18e32 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -810,7 +810,7 @@ static int __ip_append_data(struct sock *sk,
810 int copy; 810 int copy;
811 int err; 811 int err;
812 int offset = 0; 812 int offset = 0;
813 unsigned int maxfraglen, fragheaderlen; 813 unsigned int maxfraglen, fragheaderlen, maxnonfragsize;
814 int csummode = CHECKSUM_NONE; 814 int csummode = CHECKSUM_NONE;
815 struct rtable *rt = (struct rtable *)cork->dst; 815 struct rtable *rt = (struct rtable *)cork->dst;
816 816
@@ -823,8 +823,10 @@ static int __ip_append_data(struct sock *sk,
823 823
824 fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); 824 fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
825 maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; 825 maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
826 maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ?
827 mtu : 0xFFFF;
826 828
827 if (cork->length + length > 0xFFFF - fragheaderlen) { 829 if (cork->length + length > maxnonfragsize - fragheaderlen) {
828 ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, 830 ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport,
829 mtu-exthdrlen); 831 mtu-exthdrlen);
830 return -EMSGSIZE; 832 return -EMSGSIZE;
@@ -1122,7 +1124,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
1122 int mtu; 1124 int mtu;
1123 int len; 1125 int len;
1124 int err; 1126 int err;
1125 unsigned int maxfraglen, fragheaderlen, fraggap; 1127 unsigned int maxfraglen, fragheaderlen, fraggap, maxnonfragsize;
1126 1128
1127 if (inet->hdrincl) 1129 if (inet->hdrincl)
1128 return -EPERM; 1130 return -EPERM;
@@ -1146,8 +1148,10 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
1146 1148
1147 fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); 1149 fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
1148 maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; 1150 maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
1151 maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ?
1152 mtu : 0xFFFF;
1149 1153
1150 if (cork->length + size > 0xFFFF - fragheaderlen) { 1154 if (cork->length + size > maxnonfragsize - fragheaderlen) {
1151 ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu); 1155 ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu);
1152 return -EMSGSIZE; 1156 return -EMSGSIZE;
1153 } 1157 }