aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/ipv4/route.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 88ce038dd495..6fdf1c195d8e 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1183,25 +1183,39 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
1183 return dst; 1183 return dst;
1184} 1184}
1185 1185
1186static void ipv4_link_failure(struct sk_buff *skb) 1186static void ipv4_send_dest_unreach(struct sk_buff *skb)
1187{ 1187{
1188 struct ip_options opt; 1188 struct ip_options opt;
1189 struct rtable *rt;
1190 int res; 1189 int res;
1191 1190
1192 /* Recompile ip options since IPCB may not be valid anymore. 1191 /* Recompile ip options since IPCB may not be valid anymore.
1192 * Also check we have a reasonable ipv4 header.
1193 */ 1193 */
1194 memset(&opt, 0, sizeof(opt)); 1194 if (!pskb_network_may_pull(skb, sizeof(struct iphdr)) ||
1195 opt.optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr); 1195 ip_hdr(skb)->version != 4 || ip_hdr(skb)->ihl < 5)
1196 return;
1196 1197
1197 rcu_read_lock(); 1198 memset(&opt, 0, sizeof(opt));
1198 res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL); 1199 if (ip_hdr(skb)->ihl > 5) {
1199 rcu_read_unlock(); 1200 if (!pskb_network_may_pull(skb, ip_hdr(skb)->ihl * 4))
1201 return;
1202 opt.optlen = ip_hdr(skb)->ihl * 4 - sizeof(struct iphdr);
1200 1203
1201 if (res) 1204 rcu_read_lock();
1202 return; 1205 res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL);
1206 rcu_read_unlock();
1203 1207
1208 if (res)
1209 return;
1210 }
1204 __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, &opt); 1211 __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, &opt);
1212}
1213
1214static void ipv4_link_failure(struct sk_buff *skb)
1215{
1216 struct rtable *rt;
1217
1218 ipv4_send_dest_unreach(skb);
1205 1219
1206 rt = skb_rtable(skb); 1220 rt = skb_rtable(skb);
1207 if (rt) 1221 if (rt)