strip the kernel version suffix from target directories, except for brcm-2.4 (the...
[openwrt.git] / target / linux / etrax / patches / generic_2.6 / 201-multiple_default_gateways.patch
1 diff -urN linux-2.6.19.old/include/linux/netfilter_ipv4/ip_nat.h linux-2.6.19.dev/include/linux/netfilter_ipv4/ip_nat.h
2 --- linux-2.6.19.old/include/linux/netfilter_ipv4/ip_nat.h      2006-11-29 22:57:37.000000000 +0100
3 +++ linux-2.6.19.dev/include/linux/netfilter_ipv4/ip_nat.h      2006-12-14 03:13:53.000000000 +0100
4 @@ -63,6 +63,13 @@
5  
6  struct ip_conntrack;
7  
8 +/* Call input routing for SNAT-ed traffic */
9 +extern unsigned int ip_nat_route_input(unsigned int hooknum,
10 +                                      struct sk_buff **pskb,
11 +                                      const struct net_device *in,
12 +                                      const struct net_device *out,
13 +                                      int (*okfn)(struct sk_buff *));
14 +
15  /* Set up the info structure to map into this range. */
16  extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack,
17                                       const struct ip_nat_range *range,
18 diff -urN linux-2.6.19.old/include/linux/rtnetlink.h linux-2.6.19.dev/include/linux/rtnetlink.h
19 --- linux-2.6.19.old/include/linux/rtnetlink.h  2006-11-29 22:57:37.000000000 +0100
20 +++ linux-2.6.19.dev/include/linux/rtnetlink.h  2006-12-14 03:13:53.000000000 +0100
21 @@ -293,6 +293,8 @@
22  #define RTNH_F_DEAD            1       /* Nexthop is dead (used by multipath)  */
23  #define RTNH_F_PERVASIVE       2       /* Do recursive gateway lookup  */
24  #define RTNH_F_ONLINK          4       /* Gateway is forced on link    */
25 +#define RTNH_F_SUSPECT         8       /* We don't know the real state */
26 +#define RTNH_F_BADSTATE                (RTNH_F_DEAD | RTNH_F_SUSPECT)
27  
28  /* Macros to handle hexthops */
29  
30 diff -urN linux-2.6.19.old/include/net/flow.h linux-2.6.19.dev/include/net/flow.h
31 --- linux-2.6.19.old/include/net/flow.h 2006-11-29 22:57:37.000000000 +0100
32 +++ linux-2.6.19.dev/include/net/flow.h 2006-12-14 03:13:53.000000000 +0100
33 @@ -19,6 +19,8 @@
34                         __be32                  daddr;
35                         __be32                  saddr;
36                         __u32                   fwmark;
37 +                       __u32                   lsrc;
38 +                       __u32                   gw;
39                         __u8                    tos;
40                         __u8                    scope;
41                 } ip4_u;
42 @@ -48,6 +50,8 @@
43  #define fl4_dst                nl_u.ip4_u.daddr
44  #define fl4_src                nl_u.ip4_u.saddr
45  #define fl4_fwmark     nl_u.ip4_u.fwmark
46 +#define fl4_lsrc       nl_u.ip4_u.lsrc
47 +#define fl4_gw         nl_u.ip4_u.gw
48  #define fl4_tos                nl_u.ip4_u.tos
49  #define fl4_scope      nl_u.ip4_u.scope
50  
51 diff -urN linux-2.6.19.old/include/net/ip_fib.h linux-2.6.19.dev/include/net/ip_fib.h
52 --- linux-2.6.19.old/include/net/ip_fib.h       2006-11-29 22:57:37.000000000 +0100
53 +++ linux-2.6.19.dev/include/net/ip_fib.h       2006-12-14 03:13:53.000000000 +0100
54 @@ -196,7 +196,8 @@
55  
56  static inline void fib_select_default(const struct flowi *flp, struct fib_result *res)
57  {
58 -       if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
59 +       if ((FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) ||
60 +           FIB_RES_NH(*res).nh_scope == RT_SCOPE_HOST)
61                 ip_fib_main_table->tb_select_default(ip_fib_main_table, flp, res);
62  }
63  
64 @@ -212,6 +213,8 @@
65  
66  #endif /* CONFIG_IP_MULTIPLE_TABLES */
67  
68 +extern int fib_result_table(struct fib_result *res);
69 +
70  /* Exported by fib_frontend.c */
71  extern struct nla_policy rtm_ipv4_policy[];
72  extern void            ip_fib_init(void);
73 @@ -284,4 +287,6 @@
74  extern void fib_proc_exit(void);
75  #endif
76  
77 +extern rwlock_t fib_nhflags_lock;
78 +
79  #endif  /* _NET_FIB_H */
80 diff -urN linux-2.6.19.old/include/net/route.h linux-2.6.19.dev/include/net/route.h
81 --- linux-2.6.19.old/include/net/route.h        2006-11-29 22:57:37.000000000 +0100
82 +++ linux-2.6.19.dev/include/net/route.h        2006-12-14 03:13:53.000000000 +0100
83 @@ -117,6 +117,7 @@
84  extern int             ip_route_output_key(struct rtable **, struct flowi *flp);
85  extern int             ip_route_output_flow(struct rtable **rp, struct flowi *flp, struct sock *sk, int flags);
86  extern int             ip_route_input(struct sk_buff*, __be32 dst, __be32 src, u8 tos, struct net_device *devin);
87 +extern int             ip_route_input_lookup(struct sk_buff*, u32 dst, u32 src, u8 tos, struct net_device *devin, u32 lsrc);
88  extern unsigned short  ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu);
89  extern void            ip_rt_send_redirect(struct sk_buff *skb);
90  
91 diff -urN linux-2.6.19.old/net/ipv4/fib_frontend.c linux-2.6.19.dev/net/ipv4/fib_frontend.c
92 --- linux-2.6.19.old/net/ipv4/fib_frontend.c    2006-11-29 22:57:37.000000000 +0100
93 +++ linux-2.6.19.dev/net/ipv4/fib_frontend.c    2006-12-14 03:13:53.000000000 +0100
94 @@ -58,6 +58,8 @@
95  #define FIB_TABLE_HASHSZ 1
96  static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];
97  
98 +#define FIB_RES_TABLE(r) (RT_TABLE_MAIN)
99 +
100  #else
101  
102  #define FIB_TABLE_HASHSZ 256
103 @@ -100,6 +102,9 @@
104         rcu_read_unlock();
105         return NULL;
106  }
107 +
108 +#define FIB_RES_TABLE(r) (fib_result_table(r))
109 +
110  #endif /* CONFIG_IP_MULTIPLE_TABLES */
111  
112  static void fib_flush(void)
113 @@ -190,6 +195,9 @@
114                                         .tos = tos } },
115                             .iif = oif };
116         struct fib_result res;
117 +       int table;
118 +       unsigned char prefixlen;
119 +       unsigned char scope;
120         int no_addr, rpf;
121         int ret;
122  
123 @@ -211,31 +219,35 @@
124                 goto e_inval_res;
125         *spec_dst = FIB_RES_PREFSRC(res);
126         fib_combine_itag(itag, &res);
127 -#ifdef CONFIG_IP_ROUTE_MULTIPATH
128 -       if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
129 -#else
130         if (FIB_RES_DEV(res) == dev)
131 -#endif
132         {
133                 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
134                 fib_res_put(&res);
135                 return ret;
136         }
137 +       table = FIB_RES_TABLE(&res);
138 +       prefixlen = res.prefixlen;
139 +       scope = res.scope;
140         fib_res_put(&res);
141         if (no_addr)
142                 goto last_resort;
143 -       if (rpf)
144 -               goto e_inval;
145         fl.oif = dev->ifindex;
146  
147         ret = 0;
148         if (fib_lookup(&fl, &res) == 0) {
149 -               if (res.type == RTN_UNICAST) {
150 +               if (res.type == RTN_UNICAST &&
151 +                   ((table == FIB_RES_TABLE(&res) &&
152 +                     res.prefixlen >= prefixlen && res.scope >= scope) ||
153 +                    !rpf)) {
154                         *spec_dst = FIB_RES_PREFSRC(res);
155                         ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
156 +                       fib_res_put(&res);
157 +                       return ret;
158                 }
159                 fib_res_put(&res);
160         }
161 +       if (rpf)
162 +               goto e_inval;
163         return ret;
164  
165  last_resort:
166 @@ -836,9 +848,7 @@
167         switch (event) {
168         case NETDEV_UP:
169                 fib_add_ifaddr(ifa);
170 -#ifdef CONFIG_IP_ROUTE_MULTIPATH
171                 fib_sync_up(ifa->ifa_dev->dev);
172 -#endif
173                 rt_cache_flush(-1);
174                 break;
175         case NETDEV_DOWN:
176 @@ -874,9 +884,7 @@
177                 for_ifa(in_dev) {
178                         fib_add_ifaddr(ifa);
179                 } endfor_ifa(in_dev);
180 -#ifdef CONFIG_IP_ROUTE_MULTIPATH
181                 fib_sync_up(dev);
182 -#endif
183                 rt_cache_flush(-1);
184                 break;
185         case NETDEV_DOWN:
186 diff -urN linux-2.6.19.old/net/ipv4/fib_hash.c linux-2.6.19.dev/net/ipv4/fib_hash.c
187 --- linux-2.6.19.old/net/ipv4/fib_hash.c        2006-11-29 22:57:37.000000000 +0100
188 +++ linux-2.6.19.dev/net/ipv4/fib_hash.c        2006-12-14 03:13:53.000000000 +0100
189 @@ -275,30 +275,38 @@
190         return err;
191  }
192  
193 -static int fn_hash_last_dflt=-1;
194 -
195  static void
196  fn_hash_select_default(struct fib_table *tb, const struct flowi *flp, struct fib_result *res)
197  {
198 -       int order, last_idx;
199 +       int order, last_idx, last_dflt, last_nhsel;
200 +       struct fib_alias *first_fa = NULL;
201 +       struct hlist_head *head;
202         struct hlist_node *node;
203         struct fib_node *f;
204         struct fib_info *fi = NULL;
205         struct fib_info *last_resort;
206         struct fn_hash *t = (struct fn_hash*)tb->tb_data;
207 -       struct fn_zone *fz = t->fn_zones[0];
208 +       struct fn_zone *fz = t->fn_zones[res->prefixlen];
209 +       u32 k;
210  
211         if (fz == NULL)
212                 return;
213  
214 +       k = fz_key(flp->fl4_dst, fz);
215 +       last_dflt = -2;
216 +       last_nhsel = 0;
217         last_idx = -1;
218         last_resort = NULL;
219         order = -1;
220  
221         read_lock(&fib_hash_lock);
222 -       hlist_for_each_entry(f, node, &fz->fz_hash[0], fn_hash) {
223 +       head = &fz->fz_hash[fn_hash(k, fz)];
224 +       hlist_for_each_entry(f, node, head, fn_hash) {
225                 struct fib_alias *fa;
226  
227 +               if (f->fn_key != k)
228 +                       continue;
229 +
230                 list_for_each_entry(fa, &f->fn_alias, fa_list) {
231                         struct fib_info *next_fi = fa->fa_info;
232  
233 @@ -306,41 +314,52 @@
234                             fa->fa_type != RTN_UNICAST)
235                                 continue;
236  
237 +                       if (fa->fa_tos &&
238 +                           fa->fa_tos != flp->fl4_tos)
239 +                               continue;
240                         if (next_fi->fib_priority > res->fi->fib_priority)
241                                 break;
242 -                       if (!next_fi->fib_nh[0].nh_gw ||
243 -                           next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK)
244 -                               continue;
245                         fa->fa_state |= FA_S_ACCESSED;
246  
247 -                       if (fi == NULL) {
248 -                               if (next_fi != res->fi)
249 -                                       break;
250 -                       } else if (!fib_detect_death(fi, order, &last_resort,
251 -                                                    &last_idx, &fn_hash_last_dflt)) {
252 +                       if (!first_fa) {
253 +                               last_dflt = fa->fa_last_dflt;
254 +                               first_fa = fa;
255 +                       }
256 +                       if (fi && !fib_detect_death(fi, order, &last_resort,
257 +                               &last_idx, &last_dflt, &last_nhsel, flp)) {
258                                 if (res->fi)
259                                         fib_info_put(res->fi);
260                                 res->fi = fi;
261                                 atomic_inc(&fi->fib_clntref);
262 -                               fn_hash_last_dflt = order;
263 +                               first_fa->fa_last_dflt = order;
264                                 goto out;
265                         }
266                         fi = next_fi;
267                         order++;
268                 }
269 +               break;
270         }
271  
272         if (order <= 0 || fi == NULL) {
273 -               fn_hash_last_dflt = -1;
274 +               if (fi && fi->fib_nhs > 1 &&
275 +                   fib_detect_death(fi, order, &last_resort, &last_idx,
276 +                       &last_dflt, &last_nhsel, flp) &&
277 +                   last_resort == fi) {
278 +                       read_lock_bh(&fib_nhflags_lock);
279 +                       fi->fib_nh[last_nhsel].nh_flags &= ~RTNH_F_SUSPECT;
280 +                       read_unlock_bh(&fib_nhflags_lock);
281 +               }
282 +               if (first_fa) first_fa->fa_last_dflt = -1;
283                 goto out;
284         }
285  
286 -       if (!fib_detect_death(fi, order, &last_resort, &last_idx, &fn_hash_last_dflt)) {
287 +       if (!fib_detect_death(fi, order, &last_resort, &last_idx,
288 +                             &last_dflt, &last_nhsel, flp)) {
289                 if (res->fi)
290                         fib_info_put(res->fi);
291                 res->fi = fi;
292                 atomic_inc(&fi->fib_clntref);
293 -               fn_hash_last_dflt = order;
294 +               first_fa->fa_last_dflt = order;
295                 goto out;
296         }
297  
298 @@ -350,8 +369,11 @@
299                 res->fi = last_resort;
300                 if (last_resort)
301                         atomic_inc(&last_resort->fib_clntref);
302 +               read_lock_bh(&fib_nhflags_lock);
303 +               last_resort->fib_nh[last_nhsel].nh_flags &= ~RTNH_F_SUSPECT;
304 +               read_unlock_bh(&fib_nhflags_lock);
305 +               first_fa->fa_last_dflt = last_idx;
306         }
307 -       fn_hash_last_dflt = last_idx;
308  out:
309         read_unlock(&fib_hash_lock);
310  }
311 @@ -447,6 +469,7 @@
312                         write_lock_bh(&fib_hash_lock);
313                         fi_drop = fa->fa_info;
314                         fa->fa_info = fi;
315 +                       fa->fa_last_dflt = -1;
316                         fa->fa_type = cfg->fc_type;
317                         fa->fa_scope = cfg->fc_scope;
318                         state = fa->fa_state;
319 @@ -506,6 +529,7 @@
320         new_fa->fa_type = cfg->fc_type;
321         new_fa->fa_scope = cfg->fc_scope;
322         new_fa->fa_state = 0;
323 +       new_fa->fa_last_dflt = -1;
324  
325         /*
326          * Insert new entry to the list.
327 diff -urN linux-2.6.19.old/net/ipv4/fib_lookup.h linux-2.6.19.dev/net/ipv4/fib_lookup.h
328 --- linux-2.6.19.old/net/ipv4/fib_lookup.h      2006-11-29 22:57:37.000000000 +0100
329 +++ linux-2.6.19.dev/net/ipv4/fib_lookup.h      2006-12-14 03:13:53.000000000 +0100
330 @@ -9,6 +9,7 @@
331         struct list_head        fa_list;
332         struct rcu_head rcu;
333         struct fib_info         *fa_info;
334 +       int                     fa_last_dflt;
335         u8                      fa_tos;
336         u8                      fa_type;
337         u8                      fa_scope;
338 @@ -35,6 +36,7 @@
339                                         u8 tos, u32 prio);
340  extern int fib_detect_death(struct fib_info *fi, int order,
341                             struct fib_info **last_resort,
342 -                           int *last_idx, int *dflt);
343 +                           int *last_idx, int *dflt, int *last_nhsel,
344 +                           const struct flowi *flp);
345  
346  #endif /* _FIB_LOOKUP_H */
347 diff -urN linux-2.6.19.old/net/ipv4/fib_rules.c linux-2.6.19.dev/net/ipv4/fib_rules.c
348 --- linux-2.6.19.old/net/ipv4/fib_rules.c       2006-11-29 22:57:37.000000000 +0100
349 +++ linux-2.6.19.dev/net/ipv4/fib_rules.c       2006-12-14 03:13:53.000000000 +0100
350 @@ -89,6 +89,11 @@
351  }
352  #endif
353  
354 +int fib_result_table(struct fib_result *res)
355 +{
356 +       return res->r->table;
357 +}
358 +
359  int fib_lookup(struct flowi *flp, struct fib_result *res)
360  {
361         struct fib_lookup_arg arg = {
362 @@ -140,7 +145,8 @@
363  void fib_select_default(const struct flowi *flp, struct fib_result *res)
364  {
365         if (res->r && res->r->action == FR_ACT_TO_TBL &&
366 -           FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) {
367 +           ((FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) ||
368 +             FIB_RES_NH(*res).nh_scope == RT_SCOPE_HOST)) {
369                 struct fib_table *tb;
370                 if ((tb = fib_get_table(res->r->table)) != NULL)
371                         tb->tb_select_default(tb, flp, res);
372 diff -urN linux-2.6.19.old/net/ipv4/fib_semantics.c linux-2.6.19.dev/net/ipv4/fib_semantics.c
373 --- linux-2.6.19.old/net/ipv4/fib_semantics.c   2006-11-29 22:57:37.000000000 +0100
374 +++ linux-2.6.19.dev/net/ipv4/fib_semantics.c   2006-12-14 03:13:53.000000000 +0100
375 @@ -55,6 +55,7 @@
376  static struct hlist_head *fib_info_laddrhash;
377  static unsigned int fib_hash_size;
378  static unsigned int fib_info_cnt;
379 +rwlock_t fib_nhflags_lock = RW_LOCK_UNLOCKED;
380  
381  #define DEVINDEX_HASHBITS 8
382  #define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS)
383 @@ -190,7 +191,7 @@
384  #ifdef CONFIG_NET_CLS_ROUTE
385                     nh->nh_tclassid != onh->nh_tclassid ||
386  #endif
387 -                   ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD))
388 +                   ((nh->nh_flags^onh->nh_flags)&~RTNH_F_BADSTATE))
389                         return -1;
390                 onh++;
391         } endfor_nexthops(fi);
392 @@ -227,7 +228,7 @@
393                     nfi->fib_priority == fi->fib_priority &&
394                     memcmp(nfi->fib_metrics, fi->fib_metrics,
395                            sizeof(fi->fib_metrics)) == 0 &&
396 -                   ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
397 +                   ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_BADSTATE) == 0 &&
398                     (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
399                         return fi;
400         }
401 @@ -319,26 +320,70 @@
402  }
403  
404  int fib_detect_death(struct fib_info *fi, int order,
405 -                    struct fib_info **last_resort, int *last_idx, int *dflt)
406 +                    struct fib_info **last_resort, int *last_idx, int *dflt,
407 +                    int *last_nhsel, const struct flowi *flp)
408  {
409         struct neighbour *n;
410 -       int state = NUD_NONE;
411 +       int nhsel;
412 +       int state;
413 +       struct fib_nh * nh;
414 +       u32 dst;
415 +       int flag, dead = 1;
416 +
417 +       /* change_nexthops(fi) { */
418 +       for (nhsel = 0, nh = fi->fib_nh; nhsel < fi->fib_nhs; nh++, nhsel++) {
419 +               if (flp->oif && flp->oif != nh->nh_oif)
420 +                       continue;
421 +               if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw && nh->nh_gw &&
422 +                   nh->nh_scope == RT_SCOPE_LINK)
423 +                       continue;
424 +               if (nh->nh_flags & RTNH_F_DEAD)
425 +                       continue;
426  
427 -       n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev);
428 -       if (n) {
429 -               state = n->nud_state;
430 -               neigh_release(n);
431 -       }
432 -       if (state==NUD_REACHABLE)
433 -               return 0;
434 -       if ((state&NUD_VALID) && order != *dflt)
435 -               return 0;
436 -       if ((state&NUD_VALID) ||
437 -           (*last_idx<0 && order > *dflt)) {
438 -               *last_resort = fi;
439 -               *last_idx = order;
440 +               flag = 0;
441 +               if (nh->nh_dev->flags & IFF_NOARP) {
442 +                       dead = 0;
443 +                       goto setfl;
444 +               }
445 +
446 +               dst = nh->nh_gw;
447 +               if (!nh->nh_gw || nh->nh_scope != RT_SCOPE_LINK)
448 +                       dst = flp->fl4_dst;
449 +
450 +               state = NUD_NONE;
451 +               n = neigh_lookup(&arp_tbl, &dst, nh->nh_dev);
452 +               if (n) {
453 +                       state = n->nud_state;
454 +                       neigh_release(n);
455 +               }
456 +               if (state==NUD_REACHABLE ||
457 +                       ((state&NUD_VALID) && order != *dflt)) {
458 +                       dead = 0;
459 +                       goto setfl;
460 +               }
461 +               if (!(state&NUD_VALID))
462 +                       flag = 1;
463 +               if (!dead)
464 +                       goto setfl;
465 +               if ((state&NUD_VALID) ||
466 +                   (*last_idx<0 && order >= *dflt)) {
467 +                       *last_resort = fi;
468 +                       *last_idx = order;
469 +                       *last_nhsel = nhsel;
470 +               }
471 +
472 +               setfl:
473 +
474 +               read_lock_bh(&fib_nhflags_lock);
475 +               if (flag)
476 +                       nh->nh_flags |= RTNH_F_SUSPECT;
477 +               else
478 +                       nh->nh_flags &= ~RTNH_F_SUSPECT;
479 +               read_unlock_bh(&fib_nhflags_lock);
480         }
481 -       return 1;
482 +       /* } endfor_nexthops(fi) */
483 +
484 +       return dead;
485  }
486  
487  #ifdef CONFIG_IP_ROUTE_MULTIPATH
488 @@ -508,8 +553,11 @@
489                                 return -EINVAL;
490                         if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL)
491                                 return -ENODEV;
492 -                       if (!(dev->flags&IFF_UP))
493 -                               return -ENETDOWN;
494 +                       if (!(dev->flags&IFF_UP)) {
495 +                               if (fi->fib_protocol != RTPROT_STATIC)
496 +                                       return -ENETDOWN;
497 +                               nh->nh_flags |= RTNH_F_DEAD;
498 +                       }
499                         nh->nh_dev = dev;
500                         dev_hold(dev);
501                         nh->nh_scope = RT_SCOPE_LINK;
502 @@ -529,24 +577,48 @@
503                         /* It is not necessary, but requires a bit of thinking */
504                         if (fl.fl4_scope < RT_SCOPE_LINK)
505                                 fl.fl4_scope = RT_SCOPE_LINK;
506 -                       if ((err = fib_lookup(&fl, &res)) != 0)
507 -                               return err;
508 +                       err = fib_lookup(&fl, &res);
509                 }
510 -               err = -EINVAL;
511 -               if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
512 -                       goto out;
513 -               nh->nh_scope = res.scope;
514 -               nh->nh_oif = FIB_RES_OIF(res);
515 -               if ((nh->nh_dev = FIB_RES_DEV(res)) == NULL)
516 -                       goto out;
517 -               dev_hold(nh->nh_dev);
518 -               err = -ENETDOWN;
519 -               if (!(nh->nh_dev->flags & IFF_UP))
520 -                       goto out;
521 -               err = 0;
522 +               if (err) {
523 +                       struct in_device *in_dev;
524 +
525 +                       if (err != -ENETUNREACH ||
526 +                           fi->fib_protocol != RTPROT_STATIC)
527 +                               return err;
528 +
529 +                       in_dev = inetdev_by_index(nh->nh_oif);
530 +                       if (in_dev == NULL ||
531 +                           in_dev->dev->flags & IFF_UP) {
532 +                               if (in_dev)
533 +                                       in_dev_put(in_dev);
534 +                               return err;
535 +                       }
536 +                       nh->nh_flags |= RTNH_F_DEAD;
537 +                       nh->nh_scope = RT_SCOPE_LINK;
538 +                       nh->nh_dev = in_dev->dev;
539 +                       dev_hold(nh->nh_dev);
540 +                       in_dev_put(in_dev);
541 +               } else {
542 +                       err = -EINVAL;
543 +                       if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
544 +                               goto out;
545 +                       nh->nh_scope = res.scope;
546 +                       nh->nh_oif = FIB_RES_OIF(res);
547 +                       if ((nh->nh_dev = FIB_RES_DEV(res)) == NULL)
548 +                               goto out;
549 +                       dev_hold(nh->nh_dev);
550 +                       if (!(nh->nh_dev->flags & IFF_UP)) {
551 +                               if (fi->fib_protocol != RTPROT_STATIC) {
552 +                                       err = -ENETDOWN;
553 +                                       goto out;
554 +                               }
555 +                               nh->nh_flags |= RTNH_F_DEAD;
556 +                       }
557 +                       err = 0;
558  out:
559 -               fib_res_put(&res);
560 -               return err;
561 +                       fib_res_put(&res);
562 +                       return err;
563 +               }
564         } else {
565                 struct in_device *in_dev;
566  
567 @@ -557,8 +629,11 @@
568                 if (in_dev == NULL)
569                         return -ENODEV;
570                 if (!(in_dev->dev->flags&IFF_UP)) {
571 -                       in_dev_put(in_dev);
572 -                       return -ENETDOWN;
573 +                       if (fi->fib_protocol != RTPROT_STATIC) {
574 +                               in_dev_put(in_dev);
575 +                               return -ENETDOWN;
576 +                       }
577 +                       nh->nh_flags |= RTNH_F_DEAD;
578                 }
579                 nh->nh_dev = in_dev->dev;
580                 dev_hold(nh->nh_dev);
581 @@ -881,8 +956,12 @@
582                                 for_nexthops(fi) {
583                                         if (nh->nh_flags&RTNH_F_DEAD)
584                                                 continue;
585 -                                       if (!flp->oif || flp->oif == nh->nh_oif)
586 -                                               break;
587 +                                       if (flp->oif && flp->oif != nh->nh_oif)
588 +                                               continue;
589 +                                       if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw &&
590 +                                           nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK)
591 +                                               continue;
592 +                                       break;
593                                 }
594  #ifdef CONFIG_IP_ROUTE_MULTIPATH
595                                 if (nhsel < fi->fib_nhs) {
596 @@ -1056,18 +1135,29 @@
597                         prev_fi = fi;
598                         dead = 0;
599                         change_nexthops(fi) {
600 -                               if (nh->nh_flags&RTNH_F_DEAD)
601 -                                       dead++;
602 -                               else if (nh->nh_dev == dev &&
603 -                                        nh->nh_scope != scope) {
604 -                                       nh->nh_flags |= RTNH_F_DEAD;
605 +                               if (nh->nh_flags&RTNH_F_DEAD) {
606 +                                       if (fi->fib_protocol!=RTPROT_STATIC ||
607 +                                           nh->nh_dev == NULL ||
608 +                                           __in_dev_get_rtnl(nh->nh_dev) == NULL ||
609 +                                           nh->nh_dev->flags&IFF_UP)
610 +                                               dead++;
611 +                               } else if (nh->nh_dev == dev &&
612 +                                          nh->nh_scope != scope) {
613 +                                       write_lock_bh(&fib_nhflags_lock);
614  #ifdef CONFIG_IP_ROUTE_MULTIPATH
615 -                                       spin_lock_bh(&fib_multipath_lock);
616 +                                       spin_lock(&fib_multipath_lock);
617 +                                       nh->nh_flags |= RTNH_F_DEAD;
618                                         fi->fib_power -= nh->nh_power;
619                                         nh->nh_power = 0;
620 -                                       spin_unlock_bh(&fib_multipath_lock);
621 +                                       spin_unlock(&fib_multipath_lock);
622 +#else
623 +                                       nh->nh_flags |= RTNH_F_DEAD;
624  #endif
625 -                                       dead++;
626 +                                       write_unlock_bh(&fib_nhflags_lock);
627 +                                       if (fi->fib_protocol!=RTPROT_STATIC ||
628 +                                           force ||
629 +                                           __in_dev_get_rtnl(dev) == NULL)
630 +                                               dead++;
631                                 }
632  #ifdef CONFIG_IP_ROUTE_MULTIPATH
633                                 if (force > 1 && nh->nh_dev == dev) {
634 @@ -1086,11 +1176,8 @@
635         return ret;
636  }
637  
638 -#ifdef CONFIG_IP_ROUTE_MULTIPATH
639 -
640  /*
641 -   Dead device goes up. We wake up dead nexthops.
642 -   It takes sense only on multipath routes.
643 +   Dead device goes up or new address is added. We wake up dead nexthops.
644   */
645  
646  int fib_sync_up(struct net_device *dev)
647 @@ -1100,8 +1187,10 @@
648         struct hlist_head *head;
649         struct hlist_node *node;
650         struct fib_nh *nh;
651 -       int ret;
652 +       struct fib_result res;
653 +       int ret, rep;
654  
655 +repeat:
656         if (!(dev->flags&IFF_UP))
657                 return 0;
658  
659 @@ -1109,6 +1198,7 @@
660         hash = fib_devindex_hashfn(dev->ifindex);
661         head = &fib_info_devhash[hash];
662         ret = 0;
663 +       rep = 0;
664  
665         hlist_for_each_entry(nh, node, head, nh_hash) {
666                 struct fib_info *fi = nh->nh_parent;
667 @@ -1121,19 +1211,37 @@
668                 prev_fi = fi;
669                 alive = 0;
670                 change_nexthops(fi) {
671 -                       if (!(nh->nh_flags&RTNH_F_DEAD)) {
672 -                               alive++;
673 +                       if (!(nh->nh_flags&RTNH_F_DEAD))
674                                 continue;
675 -                       }
676                         if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
677                                 continue;
678                         if (nh->nh_dev != dev || !__in_dev_get_rtnl(dev))
679                                 continue;
680 +                       if (nh->nh_gw && fi->fib_protocol == RTPROT_STATIC) {
681 +                               struct flowi fl = {
682 +                                       .nl_u = { .ip4_u =
683 +                                                 { .daddr = nh->nh_gw,
684 +                                                   .scope = nh->nh_scope } },
685 +                                       .oif =  nh->nh_oif,
686 +                               };
687 +                               if (fib_lookup(&fl, &res) != 0)
688 +                                       continue;
689 +                               if (res.type != RTN_UNICAST &&
690 +                                   res.type != RTN_LOCAL) {
691 +                                       fib_res_put(&res);
692 +                                       continue;
693 +                               }
694 +                               nh->nh_scope = res.scope;
695 +                               fib_res_put(&res);
696 +                               rep = 1;
697 +                       }
698                         alive++;
699 +#ifdef CONFIG_IP_ROUTE_MULTIPATH
700                         spin_lock_bh(&fib_multipath_lock);
701                         nh->nh_power = 0;
702                         nh->nh_flags &= ~RTNH_F_DEAD;
703                         spin_unlock_bh(&fib_multipath_lock);
704 +#endif
705                 } endfor_nexthops(fi)
706  
707                 if (alive > 0) {
708 @@ -1141,10 +1249,14 @@
709                         ret++;
710                 }
711         }
712 +       if (rep)
713 +               goto repeat;
714  
715         return ret;
716  }
717  
718 +#ifdef CONFIG_IP_ROUTE_MULTIPATH
719 +
720  /*
721     The algorithm is suboptimal, but it provides really
722     fair weighted route distribution.
723 @@ -1153,24 +1265,45 @@
724  void fib_select_multipath(const struct flowi *flp, struct fib_result *res)
725  {
726         struct fib_info *fi = res->fi;
727 -       int w;
728 +       int w, alive;
729  
730         spin_lock_bh(&fib_multipath_lock);
731 +       if (flp->oif) {
732 +               int sel = -1;
733 +               w = -1;
734 +               change_nexthops(fi) {
735 +                       if (flp->oif != nh->nh_oif)
736 +                               continue;
737 +                       if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw &&
738 +                           nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK)
739 +                               continue;
740 +                       if (!(nh->nh_flags&RTNH_F_BADSTATE)) {
741 +                               if (nh->nh_power > w) {
742 +                                       w = nh->nh_power;
743 +                                       sel = nhsel;
744 +                               }
745 +                       }
746 +               } endfor_nexthops(fi);
747 +               if (sel >= 0) {
748 +                       spin_unlock_bh(&fib_multipath_lock);
749 +                       res->nh_sel = sel;
750 +                       return;
751 +               }
752 +               goto last_resort;
753 +       }
754 +
755 +repeat:
756         if (fi->fib_power <= 0) {
757                 int power = 0;
758                 change_nexthops(fi) {
759 -                       if (!(nh->nh_flags&RTNH_F_DEAD)) {
760 +                       if (!(nh->nh_flags&RTNH_F_BADSTATE)) {
761                                 power += nh->nh_weight;
762                                 nh->nh_power = nh->nh_weight;
763                         }
764                 } endfor_nexthops(fi);
765                 fi->fib_power = power;
766 -               if (power <= 0) {
767 -                       spin_unlock_bh(&fib_multipath_lock);
768 -                       /* Race condition: route has just become dead. */
769 -                       res->nh_sel = 0;
770 -                       return;
771 -               }
772 +               if (power <= 0)
773 +                       goto last_resort;
774         }
775  
776  
777 @@ -1180,20 +1313,40 @@
778  
779         w = jiffies % fi->fib_power;
780  
781 +       alive = 0;
782         change_nexthops(fi) {
783 -               if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) {
784 +               if (!(nh->nh_flags&RTNH_F_BADSTATE) && nh->nh_power) {
785                         if ((w -= nh->nh_power) <= 0) {
786                                 nh->nh_power--;
787                                 fi->fib_power--;
788 -                               res->nh_sel = nhsel;
789                                 spin_unlock_bh(&fib_multipath_lock);
790 +                               res->nh_sel = nhsel;
791                                 return;
792                         }
793 +                       alive = 1;
794 +               }
795 +       } endfor_nexthops(fi);
796 +       if (alive) {
797 +               fi->fib_power = 0;
798 +               goto repeat;
799 +       }
800 +
801 +last_resort:
802 +
803 +       for_nexthops(fi) {
804 +               if (!(nh->nh_flags&RTNH_F_DEAD)) {
805 +                       if (flp->oif && flp->oif != nh->nh_oif)
806 +                               continue;
807 +                       if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw &&
808 +                           nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK)
809 +                               continue;
810 +                       spin_unlock_bh(&fib_multipath_lock);
811 +                       res->nh_sel = nhsel;
812 +                       return;
813                 }
814         } endfor_nexthops(fi);
815  
816         /* Race condition: route has just become dead. */
817 -       res->nh_sel = 0;
818         spin_unlock_bh(&fib_multipath_lock);
819  }
820  #endif
821 diff -urN linux-2.6.19.old/net/ipv4/netfilter/ip_nat_core.c linux-2.6.19.dev/net/ipv4/netfilter/ip_nat_core.c
822 --- linux-2.6.19.old/net/ipv4/netfilter/ip_nat_core.c   2006-11-29 22:57:37.000000000 +0100
823 +++ linux-2.6.19.dev/net/ipv4/netfilter/ip_nat_core.c   2006-12-14 03:13:53.000000000 +0100
824 @@ -573,6 +573,53 @@
825  EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr);
826  #endif
827  
828 +unsigned int
829 +ip_nat_route_input(unsigned int hooknum,
830 +               struct sk_buff **pskb,
831 +               const struct net_device *in,
832 +               const struct net_device *out,
833 +               int (*okfn)(struct sk_buff *))
834 +{
835 +       struct sk_buff *skb = *pskb;
836 +       struct iphdr *iph;
837 +       struct ip_conntrack *conn;
838 +       enum ip_conntrack_info ctinfo;
839 +       enum ip_conntrack_dir dir;
840 +       unsigned long statusbit;
841 +       u32 saddr;
842 +
843 +       if (!(conn = ip_conntrack_get(skb, &ctinfo)))
844 +               return NF_ACCEPT;
845 +
846 +       if (!(conn->status & IPS_NAT_DONE_MASK))
847 +               return NF_ACCEPT;
848 +       dir = CTINFO2DIR(ctinfo);
849 +       statusbit = IPS_SRC_NAT;
850 +       if (dir == IP_CT_DIR_REPLY)
851 +               statusbit ^= IPS_NAT_MASK;
852 +       if (!(conn->status & statusbit))
853 +               return NF_ACCEPT;
854 +
855 +       if (skb->dst)
856 +               return NF_ACCEPT;
857 +
858 +       if (skb->len < sizeof(struct iphdr))
859 +               return NF_ACCEPT;
860 +
861 +       /* use daddr in other direction as masquerade address (lsrc) */
862 +       iph = skb->nh.iph;
863 +       saddr = conn->tuplehash[!dir].tuple.dst.ip;
864 +       if (saddr == iph->saddr)
865 +               return NF_ACCEPT;
866 +
867 +       if (ip_route_input_lookup(skb, iph->daddr, iph->saddr, iph->tos,
868 +           skb->dev, saddr))
869 +               return NF_DROP;
870 +
871 +       return NF_ACCEPT;
872 +}
873 +EXPORT_SYMBOL_GPL(ip_nat_route_input);
874 +
875  static int __init ip_nat_init(void)
876  {
877         size_t i;
878 diff -urN linux-2.6.19.old/net/ipv4/netfilter/ip_nat_standalone.c linux-2.6.19.dev/net/ipv4/netfilter/ip_nat_standalone.c
879 --- linux-2.6.19.old/net/ipv4/netfilter/ip_nat_standalone.c     2006-11-29 22:57:37.000000000 +0100
880 +++ linux-2.6.19.dev/net/ipv4/netfilter/ip_nat_standalone.c     2006-12-14 03:13:53.000000000 +0100
881 @@ -325,6 +325,14 @@
882                 .hooknum        = NF_IP_LOCAL_OUT,
883                 .priority       = NF_IP_PRI_NAT_DST,
884         },
885 +       /* Before routing, route before mangling */
886 +       {
887 +               .hook           = ip_nat_route_input,
888 +               .owner          = THIS_MODULE,
889 +               .pf             = PF_INET,
890 +               .hooknum        = NF_IP_PRE_ROUTING,
891 +               .priority       = NF_IP_PRI_LAST-1,
892 +       },
893         /* After packet filtering, change source */
894         {
895                 .hook           = ip_nat_fn,
896 diff -urN linux-2.6.19.old/net/ipv4/netfilter/ipt_MASQUERADE.c linux-2.6.19.dev/net/ipv4/netfilter/ipt_MASQUERADE.c
897 --- linux-2.6.19.old/net/ipv4/netfilter/ipt_MASQUERADE.c        2006-11-29 22:57:37.000000000 +0100
898 +++ linux-2.6.19.dev/net/ipv4/netfilter/ipt_MASQUERADE.c        2006-12-14 03:13:53.000000000 +0100
899 @@ -85,13 +85,31 @@
900                 return NF_ACCEPT;
901  
902         mr = targinfo;
903 -       rt = (struct rtable *)(*pskb)->dst;
904 -       newsrc = inet_select_addr(out, rt->rt_gateway, RT_SCOPE_UNIVERSE);
905 -       if (!newsrc) {
906 -               printk("MASQUERADE: %s ate my IP address\n", out->name);
907 -               return NF_DROP;
908 +
909 +       {
910 +               struct flowi fl = { .nl_u = { .ip4_u =
911 +                                             { .daddr = (*pskb)->nh.iph->daddr,
912 +                                               .tos = (RT_TOS((*pskb)->nh.iph->tos) |
913 +                                                       RTO_CONN),
914 +                                               .gw = ((struct rtable *) (*pskb)->dst)->rt_gateway,
915 +#ifdef CONFIG_IP_ROUTE_FWMARK
916 +                                               .fwmark = (*pskb)->nfmark
917 +#endif
918 +                                             } },
919 +                                   .oif = out->ifindex };
920 +               if (ip_route_output_key(&rt, &fl) != 0) {
921 +                       /* Funky routing can do this. */
922 +                       if (net_ratelimit())
923 +                               printk("MASQUERADE:"
924 +                                      " No route: Rusty's brain broke!\n");
925 +                       return NF_DROP;
926 +               }
927         }
928  
929 +       newsrc = rt->rt_src;
930 +       DEBUGP("newsrc = %u.%u.%u.%u\n", NIPQUAD(newsrc));
931 +       ip_rt_put(rt);
932 +
933         write_lock_bh(&masq_lock);
934         ct->nat.masq_index = out->ifindex;
935         write_unlock_bh(&masq_lock);
936 diff -urN linux-2.6.19.old/net/ipv4/route.c linux-2.6.19.dev/net/ipv4/route.c
937 --- linux-2.6.19.old/net/ipv4/route.c   2006-11-29 22:57:37.000000000 +0100
938 +++ linux-2.6.19.dev/net/ipv4/route.c   2006-12-14 03:13:53.000000000 +0100
939 @@ -1211,6 +1211,7 @@
940  
941                                 /* Gateway is different ... */
942                                 rt->rt_gateway          = new_gw;
943 +                               if (rt->fl.fl4_gw) rt->fl.fl4_gw = new_gw;
944  
945                                 /* Redirect received -> path was valid */
946                                 dst_confirm(&rth->u.dst);
947 @@ -1647,6 +1648,7 @@
948         rth->fl.fl4_fwmark= skb->nfmark;
949  #endif
950         rth->fl.fl4_src = saddr;
951 +       rth->fl.fl4_lsrc = 0;
952         rth->rt_src     = saddr;
953  #ifdef CONFIG_NET_CLS_ROUTE
954         rth->u.dst.tclassid = itag;
955 @@ -1657,6 +1659,7 @@
956         dev_hold(rth->u.dst.dev);
957         rth->idev       = in_dev_get(rth->u.dst.dev);
958         rth->fl.oif     = 0;
959 +       rth->fl.fl4_gw  = 0;
960         rth->rt_gateway = daddr;
961         rth->rt_spec_dst= spec_dst;
962         rth->rt_type    = RTN_MULTICAST;
963 @@ -1721,7 +1724,7 @@
964                                   struct fib_result* res, 
965                                   struct in_device *in_dev, 
966                                   __be32 daddr, __be32 saddr, u32 tos,
967 -                                 struct rtable **result) 
968 +                                 u32 lsrc, struct rtable **result) 
969  {
970  
971         struct rtable *rth;
972 @@ -1755,6 +1758,7 @@
973                 flags |= RTCF_DIRECTSRC;
974  
975         if (out_dev == in_dev && err && !(flags & (RTCF_NAT | RTCF_MASQ)) &&
976 +           !lsrc &&
977             (IN_DEV_SHARED_MEDIA(out_dev) ||
978              inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res))))
979                 flags |= RTCF_DOREDIRECT;
980 @@ -1794,6 +1798,7 @@
981  #endif
982         rth->fl.fl4_src = saddr;
983         rth->rt_src     = saddr;
984 +       rth->fl.fl4_lsrc        = lsrc;
985         rth->rt_gateway = daddr;
986         rth->rt_iif     =
987                 rth->fl.iif     = in_dev->dev->ifindex;
988 @@ -1801,6 +1806,7 @@
989         dev_hold(rth->u.dst.dev);
990         rth->idev       = in_dev_get(rth->u.dst.dev);
991         rth->fl.oif     = 0;
992 +       rth->fl.fl4_gw  = 0;
993         rth->rt_spec_dst= spec_dst;
994  
995         rth->u.dst.input = ip_forward;
996 @@ -1822,19 +1828,21 @@
997                                        struct fib_result* res, 
998                                        const struct flowi *fl,
999                                        struct in_device *in_dev,
1000 -                                      __be32 daddr, __be32 saddr, u32 tos)
1001 +                                      __be32 daddr, __be32 saddr, u32 tos, 
1002 +                                      u32 lsrc)
1003  {
1004         struct rtable* rth = NULL;
1005         int err;
1006         unsigned hash;
1007  
1008 +       fib_select_default(fl, res);
1009  #ifdef CONFIG_IP_ROUTE_MULTIPATH
1010 -       if (res->fi && res->fi->fib_nhs > 1 && fl->oif == 0)
1011 +       if (res->fi && res->fi->fib_nhs > 1)
1012                 fib_select_multipath(fl, res);
1013  #endif
1014  
1015         /* create a routing cache entry */
1016 -       err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth);
1017 +       err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, lsrc, &rth);
1018         if (err)
1019                 return err;
1020  
1021 @@ -1847,7 +1855,8 @@
1022                                    struct fib_result* res, 
1023                                    const struct flowi *fl,
1024                                    struct in_device *in_dev,
1025 -                                  __be32 daddr, __be32 saddr, u32 tos)
1026 +                                  __be32 daddr, __be32 saddr, u32 tos, 
1027 +                                  u32 lsrc)
1028  {
1029  #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
1030         struct rtable* rth = NULL, *rtres;
1031 @@ -1863,7 +1872,7 @@
1032         /* distinguish between multipath and singlepath */
1033         if (hopcount < 2)
1034                 return ip_mkroute_input_def(skb, res, fl, in_dev, daddr,
1035 -                                           saddr, tos);
1036 +                                           saddr, tos, 0);
1037         
1038         /* add all alternatives to the routing cache */
1039         for (hop = 0; hop < hopcount; hop++) {
1040 @@ -1875,7 +1884,7 @@
1041  
1042                 /* create a routing cache entry */
1043                 err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos,
1044 -                                     &rth);
1045 +                                     0, &rth);
1046                 if (err)
1047                         return err;
1048  
1049 @@ -1895,7 +1904,7 @@
1050         skb->dst = &rtres->u.dst;
1051         return err;
1052  #else /* CONFIG_IP_ROUTE_MULTIPATH_CACHED  */
1053 -       return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos);
1054 +       return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos, lsrc);
1055  #endif /* CONFIG_IP_ROUTE_MULTIPATH_CACHED  */
1056  }
1057  
1058 @@ -1911,20 +1920,20 @@
1059   */
1060  
1061  static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
1062 -                              u8 tos, struct net_device *dev)
1063 +                              u8 tos, struct net_device *dev, u32 lsrc)
1064  {
1065         struct fib_result res;
1066         struct in_device *in_dev = in_dev_get(dev);
1067         struct flowi fl = { .nl_u = { .ip4_u =
1068                                       { .daddr = daddr,
1069 -                                       .saddr = saddr,
1070 +                                       .saddr = lsrc? : saddr,
1071                                         .tos = tos,
1072                                         .scope = RT_SCOPE_UNIVERSE,
1073  #ifdef CONFIG_IP_ROUTE_FWMARK
1074                                         .fwmark = skb->nfmark
1075  #endif
1076                                       } },
1077 -                           .iif = dev->ifindex };
1078 +                           .iif = lsrc? loopback_dev.ifindex : dev->ifindex };
1079         unsigned        flags = 0;
1080         u32             itag = 0;
1081         struct rtable * rth;
1082 @@ -1957,6 +1966,12 @@
1083         if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr))
1084                 goto martian_destination;
1085  
1086 +       if (lsrc) {
1087 +               if (MULTICAST(lsrc) || BADCLASS(lsrc) ||
1088 +                   ZERONET(lsrc) || LOOPBACK(lsrc))
1089 +                       goto e_inval;
1090 +       }
1091 +
1092         /*
1093          *      Now we are ready to route packet.
1094          */
1095 @@ -1966,6 +1981,10 @@
1096                 goto no_route;
1097         }
1098         free_res = 1;
1099 +       if (lsrc && res.type != RTN_UNICAST && res.type != RTN_NAT)
1100 +               goto e_inval;
1101 +       fl.iif = dev->ifindex;
1102 +       fl.fl4_src = saddr;
1103  
1104         RT_CACHE_STAT_INC(in_slow_tot);
1105  
1106 @@ -1990,7 +2009,7 @@
1107         if (res.type != RTN_UNICAST)
1108                 goto martian_destination;
1109  
1110 -       err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos);
1111 +       err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos, lsrc);
1112         if (err == -ENOBUFS)
1113                 goto e_nobufs;
1114         if (err == -EINVAL)
1115 @@ -2005,6 +2024,8 @@
1116  brd_input:
1117         if (skb->protocol != htons(ETH_P_IP))
1118                 goto e_inval;
1119 +       if (lsrc)
1120 +               goto e_inval;
1121  
1122         if (ZERONET(saddr))
1123                 spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK);
1124 @@ -2047,6 +2068,7 @@
1125         rth->u.dst.dev  = &loopback_dev;
1126         dev_hold(rth->u.dst.dev);
1127         rth->idev       = in_dev_get(rth->u.dst.dev);
1128 +       rth->fl.fl4_gw  = 0;
1129         rth->rt_gateway = daddr;
1130         rth->rt_spec_dst= spec_dst;
1131         rth->u.dst.input= ip_local_deliver;
1132 @@ -2096,8 +2118,9 @@
1133         goto e_inval;
1134  }
1135  
1136 -int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr,
1137 -                  u8 tos, struct net_device *dev)
1138 +static inline int
1139 +ip_route_input_cached(struct sk_buff *skb, __be32 daddr, __be32 saddr,
1140 +                  u8 tos, struct net_device *dev, u32 lsrc)
1141  {
1142         struct rtable * rth;
1143         unsigned        hash;
1144 @@ -2112,6 +2135,7 @@
1145                 if (rth->fl.fl4_dst == daddr &&
1146                     rth->fl.fl4_src == saddr &&
1147                     rth->fl.iif == iif &&
1148 +                   rth->fl.fl4_lsrc == lsrc &&
1149                     rth->fl.oif == 0 &&
1150  #ifdef CONFIG_IP_ROUTE_FWMARK
1151                     rth->fl.fl4_fwmark == skb->nfmark &&
1152 @@ -2160,7 +2184,19 @@
1153                 rcu_read_unlock();
1154                 return -EINVAL;
1155         }
1156 -       return ip_route_input_slow(skb, daddr, saddr, tos, dev);
1157 +       return ip_route_input_slow(skb, daddr, saddr, tos, dev, lsrc);
1158 +}
1159 +
1160 +int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
1161 +                  u8 tos, struct net_device *dev)
1162 +{
1163 +       return ip_route_input_cached(skb, daddr, saddr, tos, dev, 0);
1164 +}
1165 +
1166 +int ip_route_input_lookup(struct sk_buff *skb, u32 daddr, u32 saddr,
1167 +                         u8 tos, struct net_device *dev, u32 lsrc)
1168 +{
1169 +       return ip_route_input_cached(skb, daddr, saddr, tos, dev, lsrc);
1170  }
1171  
1172  static inline int __mkroute_output(struct rtable **result,
1173 @@ -2239,6 +2275,7 @@
1174         rth->fl.fl4_tos = tos;
1175         rth->fl.fl4_src = oldflp->fl4_src;
1176         rth->fl.oif     = oldflp->oif;
1177 +       rth->fl.fl4_gw  = oldflp->fl4_gw;
1178  #ifdef CONFIG_IP_ROUTE_FWMARK
1179         rth->fl.fl4_fwmark= oldflp->fl4_fwmark;
1180  #endif
1181 @@ -2381,6 +2418,7 @@
1182         struct flowi fl = { .nl_u = { .ip4_u =
1183                                       { .daddr = oldflp->fl4_dst,
1184                                         .saddr = oldflp->fl4_src,
1185 +                                       .gw = oldflp->fl4_gw,
1186                                         .tos = tos & IPTOS_RT_MASK,
1187                                         .scope = ((tos & RTO_ONLINK) ?
1188                                                   RT_SCOPE_LINK :
1189 @@ -2486,6 +2524,7 @@
1190                 dev_out = &loopback_dev;
1191                 dev_hold(dev_out);
1192                 fl.oif = loopback_dev.ifindex;
1193 +               fl.fl4_gw = 0;
1194                 res.type = RTN_LOCAL;
1195                 flags |= RTCF_LOCAL;
1196                 goto make_route;
1197 @@ -2493,7 +2532,7 @@
1198  
1199         if (fib_lookup(&fl, &res)) {
1200                 res.fi = NULL;
1201 -               if (oldflp->oif) {
1202 +               if (oldflp->oif && dev_out->flags & IFF_UP) {
1203                         /* Apparently, routing tables are wrong. Assume,
1204                            that the destination is on link.
1205  
1206 @@ -2533,6 +2572,7 @@
1207                 dev_out = &loopback_dev;
1208                 dev_hold(dev_out);
1209                 fl.oif = dev_out->ifindex;
1210 +               fl.fl4_gw = 0;
1211                 if (res.fi)
1212                         fib_info_put(res.fi);
1213                 res.fi = NULL;
1214 @@ -2540,13 +2580,12 @@
1215                 goto make_route;
1216         }
1217  
1218 +       if (res.type == RTN_UNICAST)
1219 +               fib_select_default(&fl, &res);
1220  #ifdef CONFIG_IP_ROUTE_MULTIPATH
1221 -       if (res.fi->fib_nhs > 1 && fl.oif == 0)
1222 +       if (res.fi->fib_nhs > 1)
1223                 fib_select_multipath(&fl, &res);
1224 -       else
1225  #endif
1226 -       if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif)
1227 -               fib_select_default(&fl, &res);
1228  
1229         if (!fl.fl4_src)
1230                 fl.fl4_src = FIB_RES_PREFSRC(res);
1231 @@ -2583,6 +2622,7 @@
1232                     rth->fl.fl4_src == flp->fl4_src &&
1233                     rth->fl.iif == 0 &&
1234                     rth->fl.oif == flp->oif &&
1235 +                   rth->fl.fl4_gw == flp->fl4_gw &&
1236  #ifdef CONFIG_IP_ROUTE_FWMARK
1237                     rth->fl.fl4_fwmark == flp->fl4_fwmark &&
1238  #endif
1239 @@ -3221,3 +3261,4 @@
1240  EXPORT_SYMBOL(__ip_select_ident);
1241  EXPORT_SYMBOL(ip_route_input);
1242  EXPORT_SYMBOL(ip_route_output_key);
1243 +EXPORT_SYMBOL(ip_route_input_lookup);