Skip to content

Commit f39925d

Browse files
committed
ipv4: Cache learned redirect information in inetpeer.
Note that we do not generate the redirect netevent any longer, because we don't create a new cached route. Instead, once the new neighbour is bound to the cached route, we emit a neigh update event instead. Signed-off-by: David S. Miller <[email protected]>
1 parent 2c8cec5 commit f39925d

File tree

1 file changed

+42
-94
lines changed

1 file changed

+42
-94
lines changed

net/ipv4/route.c

Lines changed: 42 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,13 +1294,8 @@ static void rt_del(unsigned hash, struct rtable *rt)
12941294
void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
12951295
__be32 saddr, struct net_device *dev)
12961296
{
1297-
int i, k;
12981297
struct in_device *in_dev = __in_dev_get_rcu(dev);
1299-
struct rtable *rth;
1300-
struct rtable __rcu **rthp;
1301-
__be32 skeys[2] = { saddr, 0 };
1302-
int ikeys[2] = { dev->ifindex, 0 };
1303-
struct netevent_redirect netevent;
1298+
struct inet_peer *peer;
13041299
struct net *net;
13051300

13061301
if (!in_dev)
@@ -1312,9 +1307,6 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
13121307
ipv4_is_zeronet(new_gw))
13131308
goto reject_redirect;
13141309

1315-
if (!rt_caching(net))
1316-
goto reject_redirect;
1317-
13181310
if (!IN_DEV_SHARED_MEDIA(in_dev)) {
13191311
if (!inet_addr_onlink(in_dev, new_gw, old_gw))
13201312
goto reject_redirect;
@@ -1325,93 +1317,13 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
13251317
goto reject_redirect;
13261318
}
13271319

1328-
for (i = 0; i < 2; i++) {
1329-
for (k = 0; k < 2; k++) {
1330-
unsigned hash = rt_hash(daddr, skeys[i], ikeys[k],
1331-
rt_genid(net));
1332-
1333-
rthp = &rt_hash_table[hash].chain;
1334-
1335-
while ((rth = rcu_dereference(*rthp)) != NULL) {
1336-
struct rtable *rt;
1337-
1338-
if (rth->fl.fl4_dst != daddr ||
1339-
rth->fl.fl4_src != skeys[i] ||
1340-
rth->fl.oif != ikeys[k] ||
1341-
rt_is_input_route(rth) ||
1342-
rt_is_expired(rth) ||
1343-
!net_eq(dev_net(rth->dst.dev), net)) {
1344-
rthp = &rth->dst.rt_next;
1345-
continue;
1346-
}
1347-
1348-
if (rth->rt_dst != daddr ||
1349-
rth->rt_src != saddr ||
1350-
rth->dst.error ||
1351-
rth->rt_gateway != old_gw ||
1352-
rth->dst.dev != dev)
1353-
break;
1354-
1355-
dst_hold(&rth->dst);
1356-
1357-
rt = dst_alloc(&ipv4_dst_ops);
1358-
if (rt == NULL) {
1359-
ip_rt_put(rth);
1360-
return;
1361-
}
1362-
1363-
/* Copy all the information. */
1364-
*rt = *rth;
1365-
rt->dst.__use = 1;
1366-
atomic_set(&rt->dst.__refcnt, 1);
1367-
rt->dst.child = NULL;
1368-
if (rt->dst.dev)
1369-
dev_hold(rt->dst.dev);
1370-
rt->dst.obsolete = -1;
1371-
rt->dst.lastuse = jiffies;
1372-
rt->dst.path = &rt->dst;
1373-
rt->dst.neighbour = NULL;
1374-
rt->dst.hh = NULL;
1375-
#ifdef CONFIG_XFRM
1376-
rt->dst.xfrm = NULL;
1377-
#endif
1378-
rt->rt_genid = rt_genid(net);
1379-
rt->rt_flags |= RTCF_REDIRECTED;
1380-
1381-
/* Gateway is different ... */
1382-
rt->rt_gateway = new_gw;
1383-
1384-
/* Redirect received -> path was valid */
1385-
dst_confirm(&rth->dst);
1386-
1387-
if (rt->peer)
1388-
atomic_inc(&rt->peer->refcnt);
1389-
if (rt->fi)
1390-
atomic_inc(&rt->fi->fib_clntref);
1391-
1392-
if (arp_bind_neighbour(&rt->dst) ||
1393-
!(rt->dst.neighbour->nud_state &
1394-
NUD_VALID)) {
1395-
if (rt->dst.neighbour)
1396-
neigh_event_send(rt->dst.neighbour, NULL);
1397-
ip_rt_put(rth);
1398-
rt_drop(rt);
1399-
goto do_next;
1400-
}
1320+
peer = inet_getpeer_v4(daddr, 1);
1321+
if (peer) {
1322+
peer->redirect_learned.a4 = new_gw;
14011323

1402-
netevent.old = &rth->dst;
1403-
netevent.new = &rt->dst;
1404-
call_netevent_notifiers(NETEVENT_REDIRECT,
1405-
&netevent);
1324+
inet_putpeer(peer);
14061325

1407-
rt_del(hash, rth);
1408-
if (!rt_intern_hash(hash, rt, &rt, NULL, rt->fl.oif))
1409-
ip_rt_put(rt);
1410-
goto do_next;
1411-
}
1412-
do_next:
1413-
;
1414-
}
1326+
atomic_inc(&__rt_peer_genid);
14151327
}
14161328
return;
14171329

@@ -1678,6 +1590,31 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
16781590
}
16791591
}
16801592

1593+
static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer)
1594+
{
1595+
struct rtable *rt = (struct rtable *) dst;
1596+
__be32 orig_gw = rt->rt_gateway;
1597+
1598+
dst_confirm(&rt->dst);
1599+
1600+
neigh_release(rt->dst.neighbour);
1601+
rt->dst.neighbour = NULL;
1602+
1603+
rt->rt_gateway = peer->redirect_learned.a4;
1604+
if (arp_bind_neighbour(&rt->dst) ||
1605+
!(rt->dst.neighbour->nud_state & NUD_VALID)) {
1606+
if (rt->dst.neighbour)
1607+
neigh_event_send(rt->dst.neighbour, NULL);
1608+
rt->rt_gateway = orig_gw;
1609+
return -EAGAIN;
1610+
} else {
1611+
rt->rt_flags |= RTCF_REDIRECTED;
1612+
call_netevent_notifiers(NETEVENT_NEIGH_UPDATE,
1613+
rt->dst.neighbour);
1614+
}
1615+
return 0;
1616+
}
1617+
16811618
static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
16821619
{
16831620
struct rtable *rt = (struct rtable *) dst;
@@ -1694,6 +1631,12 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
16941631
if (peer && peer->pmtu_expires)
16951632
check_peer_pmtu(dst, peer);
16961633

1634+
if (peer && peer->redirect_learned.a4 &&
1635+
peer->redirect_learned.a4 != rt->rt_gateway) {
1636+
if (check_peer_redir(dst, peer))
1637+
return NULL;
1638+
}
1639+
16971640
rt->rt_peer_genid = rt_peer_genid();
16981641
}
16991642
return dst;
@@ -1830,6 +1773,11 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi)
18301773

18311774
if (peer->pmtu_expires)
18321775
check_peer_pmtu(&rt->dst, peer);
1776+
if (peer->redirect_learned.a4 &&
1777+
peer->redirect_learned.a4 != rt->rt_gateway) {
1778+
rt->rt_gateway = peer->redirect_learned.a4;
1779+
rt->rt_flags |= RTCF_REDIRECTED;
1780+
}
18331781
} else {
18341782
if (fi->fib_metrics != (u32 *) dst_default_metrics) {
18351783
rt->fi = fi;

0 commit comments

Comments
 (0)