diff options
-rw-r--r-- | drivers/net/xen-netback/netback.c | 236 | ||||
-rw-r--r-- | include/linux/ipv6.h | 1 | ||||
-rw-r--r-- | include/net/ipv6.h | 3 |
3 files changed, 140 insertions, 100 deletions
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 64f0e0d18b81..acf13920e6d1 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c | |||
@@ -1149,49 +1149,72 @@ static int xenvif_set_skb_gso(struct xenvif *vif, | |||
1149 | return 0; | 1149 | return 0; |
1150 | } | 1150 | } |
1151 | 1151 | ||
1152 | static inline void maybe_pull_tail(struct sk_buff *skb, unsigned int len) | 1152 | static inline int maybe_pull_tail(struct sk_buff *skb, unsigned int len, |
1153 | unsigned int max) | ||
1153 | { | 1154 | { |
1154 | if (skb_is_nonlinear(skb) && skb_headlen(skb) < len) { | 1155 | if (skb_headlen(skb) >= len) |
1155 | /* If we need to pullup then pullup to the max, so we | 1156 | return 0; |
1156 | * won't need to do it again. | 1157 | |
1157 | */ | 1158 | /* If we need to pullup then pullup to the max, so we |
1158 | int target = min_t(int, skb->len, MAX_TCP_HEADER); | 1159 | * won't need to do it again. |
1159 | __pskb_pull_tail(skb, target - skb_headlen(skb)); | 1160 | */ |
1160 | } | 1161 | if (max > skb->len) |
1162 | max = skb->len; | ||
1163 | |||
1164 | if (__pskb_pull_tail(skb, max - skb_headlen(skb)) == NULL) | ||
1165 | return -ENOMEM; | ||
1166 | |||
1167 | if (skb_headlen(skb) < len) | ||
1168 | return -EPROTO; | ||
1169 | |||
1170 | return 0; | ||
1161 | } | 1171 | } |
1162 | 1172 | ||
1173 | /* This value should be large enough to cover a tagged ethernet header plus | ||
1174 | * maximally sized IP and TCP or UDP headers. | ||
1175 | */ | ||
1176 | #define MAX_IP_HDR_LEN 128 | ||
1177 | |||
1163 | static int checksum_setup_ip(struct xenvif *vif, struct sk_buff *skb, | 1178 | static int checksum_setup_ip(struct xenvif *vif, struct sk_buff *skb, |
1164 | int recalculate_partial_csum) | 1179 | int recalculate_partial_csum) |
1165 | { | 1180 | { |
1166 | struct iphdr *iph = (void *)skb->data; | ||
1167 | unsigned int header_size; | ||
1168 | unsigned int off; | 1181 | unsigned int off; |
1169 | int err = -EPROTO; | 1182 | bool fragment; |
1183 | int err; | ||
1184 | |||
1185 | fragment = false; | ||
1186 | |||
1187 | err = maybe_pull_tail(skb, | ||
1188 | sizeof(struct iphdr), | ||
1189 | MAX_IP_HDR_LEN); | ||
1190 | if (err < 0) | ||
1191 | goto out; | ||
1170 | 1192 | ||
1171 | off = sizeof(struct iphdr); | 1193 | if (ip_hdr(skb)->frag_off & htons(IP_OFFSET | IP_MF)) |
1194 | fragment = true; | ||
1172 | 1195 | ||
1173 | header_size = skb->network_header + off + MAX_IPOPTLEN; | 1196 | off = ip_hdrlen(skb); |
1174 | maybe_pull_tail(skb, header_size); | ||
1175 | 1197 | ||
1176 | off = iph->ihl * 4; | 1198 | err = -EPROTO; |
1177 | 1199 | ||
1178 | switch (iph->protocol) { | 1200 | switch (ip_hdr(skb)->protocol) { |
1179 | case IPPROTO_TCP: | 1201 | case IPPROTO_TCP: |
1180 | if (!skb_partial_csum_set(skb, off, | 1202 | if (!skb_partial_csum_set(skb, off, |
1181 | offsetof(struct tcphdr, check))) | 1203 | offsetof(struct tcphdr, check))) |
1182 | goto out; | 1204 | goto out; |
1183 | 1205 | ||
1184 | if (recalculate_partial_csum) { | 1206 | if (recalculate_partial_csum) { |
1185 | struct tcphdr *tcph = tcp_hdr(skb); | 1207 | err = maybe_pull_tail(skb, |
1186 | 1208 | off + sizeof(struct tcphdr), | |
1187 | header_size = skb->network_header + | 1209 | MAX_IP_HDR_LEN); |
1188 | off + | 1210 | if (err < 0) |
1189 | sizeof(struct tcphdr); | 1211 | goto out; |
1190 | maybe_pull_tail(skb, header_size); | 1212 | |
1191 | 1213 | tcp_hdr(skb)->check = | |
1192 | tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, | 1214 | ~csum_tcpudp_magic(ip_hdr(skb)->saddr, |
1193 | skb->len - off, | 1215 | ip_hdr(skb)->daddr, |
1194 | IPPROTO_TCP, 0); | 1216 | skb->len - off, |
1217 | IPPROTO_TCP, 0); | ||
1195 | } | 1218 | } |
1196 | break; | 1219 | break; |
1197 | case IPPROTO_UDP: | 1220 | case IPPROTO_UDP: |
@@ -1200,24 +1223,20 @@ static int checksum_setup_ip(struct xenvif *vif, struct sk_buff *skb, | |||
1200 | goto out; | 1223 | goto out; |
1201 | 1224 | ||
1202 | if (recalculate_partial_csum) { | 1225 | if (recalculate_partial_csum) { |
1203 | struct udphdr *udph = udp_hdr(skb); | 1226 | err = maybe_pull_tail(skb, |
1204 | 1227 | off + sizeof(struct udphdr), | |
1205 | header_size = skb->network_header + | 1228 | MAX_IP_HDR_LEN); |
1206 | off + | 1229 | if (err < 0) |
1207 | sizeof(struct udphdr); | 1230 | goto out; |
1208 | maybe_pull_tail(skb, header_size); | 1231 | |
1209 | 1232 | udp_hdr(skb)->check = | |
1210 | udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, | 1233 | ~csum_tcpudp_magic(ip_hdr(skb)->saddr, |
1211 | skb->len - off, | 1234 | ip_hdr(skb)->daddr, |
1212 | IPPROTO_UDP, 0); | 1235 | skb->len - off, |
1236 | IPPROTO_UDP, 0); | ||
1213 | } | 1237 | } |
1214 | break; | 1238 | break; |
1215 | default: | 1239 | default: |
1216 | if (net_ratelimit()) | ||
1217 | netdev_err(vif->dev, | ||
1218 | "Attempting to checksum a non-TCP/UDP packet, " | ||
1219 | "dropping a protocol %d packet\n", | ||
1220 | iph->protocol); | ||
1221 | goto out; | 1240 | goto out; |
1222 | } | 1241 | } |
1223 | 1242 | ||
@@ -1227,75 +1246,99 @@ out: | |||
1227 | return err; | 1246 | return err; |
1228 | } | 1247 | } |
1229 | 1248 | ||
1249 | /* This value should be large enough to cover a tagged ethernet header plus | ||
1250 | * an IPv6 header, all options, and a maximal TCP or UDP header. | ||
1251 | */ | ||
1252 | #define MAX_IPV6_HDR_LEN 256 | ||
1253 | |||
1254 | #define OPT_HDR(type, skb, off) \ | ||
1255 | (type *)(skb_network_header(skb) + (off)) | ||
1256 | |||
1230 | static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb, | 1257 | static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb, |
1231 | int recalculate_partial_csum) | 1258 | int recalculate_partial_csum) |
1232 | { | 1259 | { |
1233 | int err = -EPROTO; | 1260 | int err; |
1234 | struct ipv6hdr *ipv6h = (void *)skb->data; | ||
1235 | u8 nexthdr; | 1261 | u8 nexthdr; |
1236 | unsigned int header_size; | ||
1237 | unsigned int off; | 1262 | unsigned int off; |
1263 | unsigned int len; | ||
1238 | bool fragment; | 1264 | bool fragment; |
1239 | bool done; | 1265 | bool done; |
1240 | 1266 | ||
1267 | fragment = false; | ||
1241 | done = false; | 1268 | done = false; |
1242 | 1269 | ||
1243 | off = sizeof(struct ipv6hdr); | 1270 | off = sizeof(struct ipv6hdr); |
1244 | 1271 | ||
1245 | header_size = skb->network_header + off; | 1272 | err = maybe_pull_tail(skb, off, MAX_IPV6_HDR_LEN); |
1246 | maybe_pull_tail(skb, header_size); | 1273 | if (err < 0) |
1274 | goto out; | ||
1247 | 1275 | ||
1248 | nexthdr = ipv6h->nexthdr; | 1276 | nexthdr = ipv6_hdr(skb)->nexthdr; |
1249 | 1277 | ||
1250 | while ((off <= sizeof(struct ipv6hdr) + ntohs(ipv6h->payload_len)) && | 1278 | len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len); |
1251 | !done) { | 1279 | while (off <= len && !done) { |
1252 | switch (nexthdr) { | 1280 | switch (nexthdr) { |
1253 | case IPPROTO_DSTOPTS: | 1281 | case IPPROTO_DSTOPTS: |
1254 | case IPPROTO_HOPOPTS: | 1282 | case IPPROTO_HOPOPTS: |
1255 | case IPPROTO_ROUTING: { | 1283 | case IPPROTO_ROUTING: { |
1256 | struct ipv6_opt_hdr *hp = (void *)(skb->data + off); | 1284 | struct ipv6_opt_hdr *hp; |
1257 | 1285 | ||
1258 | header_size = skb->network_header + | 1286 | err = maybe_pull_tail(skb, |
1259 | off + | 1287 | off + |
1260 | sizeof(struct ipv6_opt_hdr); | 1288 | sizeof(struct ipv6_opt_hdr), |
1261 | maybe_pull_tail(skb, header_size); | 1289 | MAX_IPV6_HDR_LEN); |
1290 | if (err < 0) | ||
1291 | goto out; | ||
1262 | 1292 | ||
1293 | hp = OPT_HDR(struct ipv6_opt_hdr, skb, off); | ||
1263 | nexthdr = hp->nexthdr; | 1294 | nexthdr = hp->nexthdr; |
1264 | off += ipv6_optlen(hp); | 1295 | off += ipv6_optlen(hp); |
1265 | break; | 1296 | break; |
1266 | } | 1297 | } |
1267 | case IPPROTO_AH: { | 1298 | case IPPROTO_AH: { |
1268 | struct ip_auth_hdr *hp = (void *)(skb->data + off); | 1299 | struct ip_auth_hdr *hp; |
1300 | |||
1301 | err = maybe_pull_tail(skb, | ||
1302 | off + | ||
1303 | sizeof(struct ip_auth_hdr), | ||
1304 | MAX_IPV6_HDR_LEN); | ||
1305 | if (err < 0) | ||
1306 | goto out; | ||
1307 | |||
1308 | hp = OPT_HDR(struct ip_auth_hdr, skb, off); | ||
1309 | nexthdr = hp->nexthdr; | ||
1310 | off += ipv6_authlen(hp); | ||
1311 | break; | ||
1312 | } | ||
1313 | case IPPROTO_FRAGMENT: { | ||
1314 | struct frag_hdr *hp; | ||
1269 | 1315 | ||
1270 | header_size = skb->network_header + | 1316 | err = maybe_pull_tail(skb, |
1271 | off + | 1317 | off + |
1272 | sizeof(struct ip_auth_hdr); | 1318 | sizeof(struct frag_hdr), |
1273 | maybe_pull_tail(skb, header_size); | 1319 | MAX_IPV6_HDR_LEN); |
1320 | if (err < 0) | ||
1321 | goto out; | ||
1322 | |||
1323 | hp = OPT_HDR(struct frag_hdr, skb, off); | ||
1324 | |||
1325 | if (hp->frag_off & htons(IP6_OFFSET | IP6_MF)) | ||
1326 | fragment = true; | ||
1274 | 1327 | ||
1275 | nexthdr = hp->nexthdr; | 1328 | nexthdr = hp->nexthdr; |
1276 | off += (hp->hdrlen+2)<<2; | 1329 | off += sizeof(struct frag_hdr); |
1277 | break; | 1330 | break; |
1278 | } | 1331 | } |
1279 | case IPPROTO_FRAGMENT: | ||
1280 | fragment = true; | ||
1281 | /* fall through */ | ||
1282 | default: | 1332 | default: |
1283 | done = true; | 1333 | done = true; |
1284 | break; | 1334 | break; |
1285 | } | 1335 | } |
1286 | } | 1336 | } |
1287 | 1337 | ||
1288 | if (!done) { | 1338 | err = -EPROTO; |
1289 | if (net_ratelimit()) | ||
1290 | netdev_err(vif->dev, "Failed to parse packet header\n"); | ||
1291 | goto out; | ||
1292 | } | ||
1293 | 1339 | ||
1294 | if (fragment) { | 1340 | if (!done || fragment) |
1295 | if (net_ratelimit()) | ||
1296 | netdev_err(vif->dev, "Packet is a fragment!\n"); | ||
1297 | goto out; | 1341 | goto out; |
1298 | } | ||
1299 | 1342 | ||
1300 | switch (nexthdr) { | 1343 | switch (nexthdr) { |
1301 | case IPPROTO_TCP: | 1344 | case IPPROTO_TCP: |
@@ -1304,17 +1347,17 @@ static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb, | |||
1304 | goto out; | 1347 | goto out; |
1305 | 1348 | ||
1306 | if (recalculate_partial_csum) { | 1349 | if (recalculate_partial_csum) { |
1307 | struct tcphdr *tcph = tcp_hdr(skb); | 1350 | err = maybe_pull_tail(skb, |
1308 | 1351 | off + sizeof(struct tcphdr), | |
1309 | header_size = skb->network_header + | 1352 | MAX_IPV6_HDR_LEN); |
1310 | off + | 1353 | if (err < 0) |
1311 | sizeof(struct tcphdr); | 1354 | goto out; |
1312 | maybe_pull_tail(skb, header_size); | 1355 | |
1313 | 1356 | tcp_hdr(skb)->check = | |
1314 | tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, | 1357 | ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, |
1315 | &ipv6h->daddr, | 1358 | &ipv6_hdr(skb)->daddr, |
1316 | skb->len - off, | 1359 | skb->len - off, |
1317 | IPPROTO_TCP, 0); | 1360 | IPPROTO_TCP, 0); |
1318 | } | 1361 | } |
1319 | break; | 1362 | break; |
1320 | case IPPROTO_UDP: | 1363 | case IPPROTO_UDP: |
@@ -1323,25 +1366,20 @@ static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb, | |||
1323 | goto out; | 1366 | goto out; |
1324 | 1367 | ||
1325 | if (recalculate_partial_csum) { | 1368 | if (recalculate_partial_csum) { |
1326 | struct udphdr *udph = udp_hdr(skb); | 1369 | err = maybe_pull_tail(skb, |
1327 | 1370 | off + sizeof(struct udphdr), | |
1328 | header_size = skb->network_header + | 1371 | MAX_IPV6_HDR_LEN); |
1329 | off + | 1372 | if (err < 0) |
1330 | sizeof(struct udphdr); | 1373 | goto out; |
1331 | maybe_pull_tail(skb, header_size); | 1374 | |
1332 | 1375 | udp_hdr(skb)->check = | |
1333 | udph->check = ~csum_ipv6_magic(&ipv6h->saddr, | 1376 | ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, |
1334 | &ipv6h->daddr, | 1377 | &ipv6_hdr(skb)->daddr, |
1335 | skb->len - off, | 1378 | skb->len - off, |
1336 | IPPROTO_UDP, 0); | 1379 | IPPROTO_UDP, 0); |
1337 | } | 1380 | } |
1338 | break; | 1381 | break; |
1339 | default: | 1382 | default: |
1340 | if (net_ratelimit()) | ||
1341 | netdev_err(vif->dev, | ||
1342 | "Attempting to checksum a non-TCP/UDP packet, " | ||
1343 | "dropping a protocol %d packet\n", | ||
1344 | nexthdr); | ||
1345 | goto out; | 1383 | goto out; |
1346 | } | 1384 | } |
1347 | 1385 | ||
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 5d89d1b808a6..c56c350324e4 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <uapi/linux/ipv6.h> | 4 | #include <uapi/linux/ipv6.h> |
5 | 5 | ||
6 | #define ipv6_optlen(p) (((p)->hdrlen+1) << 3) | 6 | #define ipv6_optlen(p) (((p)->hdrlen+1) << 3) |
7 | #define ipv6_authlen(p) (((p)->hdrlen+2) << 2) | ||
7 | /* | 8 | /* |
8 | * This structure contains configuration options per IPv6 link. | 9 | * This structure contains configuration options per IPv6 link. |
9 | */ | 10 | */ |
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index eb198acaac1d..488316e339a1 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -110,7 +110,8 @@ struct frag_hdr { | |||
110 | __be32 identification; | 110 | __be32 identification; |
111 | }; | 111 | }; |
112 | 112 | ||
113 | #define IP6_MF 0x0001 | 113 | #define IP6_MF 0x0001 |
114 | #define IP6_OFFSET 0xFFF8 | ||
114 | 115 | ||
115 | #include <net/sock.h> | 116 | #include <net/sock.h> |
116 | 117 | ||