aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-11-05 17:40:58 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-01-30 02:05:25 -0500
commit67a391d72ca7efb387c30ec761a487e50a3ff085 (patch)
treea3594508502769d56e838c20b5b094bcc1906b16
parent66af1e558538137080615e7ad6d1f2f80862de01 (diff)
SUNRPC: Fix TCP rebinding logic
Currently the TCP rebinding logic assumes that if we're not using a reserved port, then we don't need to reconnect on the same port if a disconnection event occurs. This breaks most RPC duplicate reply cache implementations. Also take into account the fact that xprt_min_resvport and xprt_max_resvport may change while we're reconnecting, since the user may change them at any time via the sysctls. Ensure that we check the port boundaries every time we loop in xs_bind4/xs_bind6. Also ensure that if the boundaries change, we only scan the ports a maximum of 2 times. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--net/sunrpc/xprtsock.c59
1 files changed, 38 insertions, 21 deletions
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index abb40c140738..b5544514ee57 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1276,34 +1276,53 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
1276 } 1276 }
1277} 1277}
1278 1278
1279static unsigned short xs_get_srcport(struct sock_xprt *transport, struct socket *sock)
1280{
1281 unsigned short port = transport->port;
1282
1283 if (port == 0 && transport->xprt.resvport)
1284 port = xs_get_random_port();
1285 return port;
1286}
1287
1288static unsigned short xs_next_srcport(struct sock_xprt *transport, struct socket *sock, unsigned short port)
1289{
1290 if (transport->port != 0)
1291 transport->port = 0;
1292 if (!transport->xprt.resvport)
1293 return 0;
1294 if (port <= xprt_min_resvport || port > xprt_max_resvport)
1295 return xprt_max_resvport;
1296 return --port;
1297}
1298
1279static int xs_bind4(struct sock_xprt *transport, struct socket *sock) 1299static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
1280{ 1300{
1281 struct sockaddr_in myaddr = { 1301 struct sockaddr_in myaddr = {
1282 .sin_family = AF_INET, 1302 .sin_family = AF_INET,
1283 }; 1303 };
1284 struct sockaddr_in *sa; 1304 struct sockaddr_in *sa;
1285 int err; 1305 int err, nloop = 0;
1286 unsigned short port = transport->port; 1306 unsigned short port = xs_get_srcport(transport, sock);
1307 unsigned short last;
1287 1308
1288 if (!transport->xprt.resvport)
1289 port = 0;
1290 sa = (struct sockaddr_in *)&transport->addr; 1309 sa = (struct sockaddr_in *)&transport->addr;
1291 myaddr.sin_addr = sa->sin_addr; 1310 myaddr.sin_addr = sa->sin_addr;
1292 do { 1311 do {
1293 myaddr.sin_port = htons(port); 1312 myaddr.sin_port = htons(port);
1294 err = kernel_bind(sock, (struct sockaddr *) &myaddr, 1313 err = kernel_bind(sock, (struct sockaddr *) &myaddr,
1295 sizeof(myaddr)); 1314 sizeof(myaddr));
1296 if (!transport->xprt.resvport) 1315 if (port == 0)
1297 break; 1316 break;
1298 if (err == 0) { 1317 if (err == 0) {
1299 transport->port = port; 1318 transport->port = port;
1300 break; 1319 break;
1301 } 1320 }
1302 if (port <= xprt_min_resvport) 1321 last = port;
1303 port = xprt_max_resvport; 1322 port = xs_next_srcport(transport, sock, port);
1304 else 1323 if (port > last)
1305 port--; 1324 nloop++;
1306 } while (err == -EADDRINUSE && port != transport->port); 1325 } while (err == -EADDRINUSE && nloop != 2);
1307 dprintk("RPC: %s "NIPQUAD_FMT":%u: %s (%d)\n", 1326 dprintk("RPC: %s "NIPQUAD_FMT":%u: %s (%d)\n",
1308 __FUNCTION__, NIPQUAD(myaddr.sin_addr), 1327 __FUNCTION__, NIPQUAD(myaddr.sin_addr),
1309 port, err ? "failed" : "ok", err); 1328 port, err ? "failed" : "ok", err);
@@ -1316,28 +1335,27 @@ static int xs_bind6(struct sock_xprt *transport, struct socket *sock)
1316 .sin6_family = AF_INET6, 1335 .sin6_family = AF_INET6,
1317 }; 1336 };
1318 struct sockaddr_in6 *sa; 1337 struct sockaddr_in6 *sa;
1319 int err; 1338 int err, nloop = 0;
1320 unsigned short port = transport->port; 1339 unsigned short port = xs_get_srcport(transport, sock);
1340 unsigned short last;
1321 1341
1322 if (!transport->xprt.resvport)
1323 port = 0;
1324 sa = (struct sockaddr_in6 *)&transport->addr; 1342 sa = (struct sockaddr_in6 *)&transport->addr;
1325 myaddr.sin6_addr = sa->sin6_addr; 1343 myaddr.sin6_addr = sa->sin6_addr;
1326 do { 1344 do {
1327 myaddr.sin6_port = htons(port); 1345 myaddr.sin6_port = htons(port);
1328 err = kernel_bind(sock, (struct sockaddr *) &myaddr, 1346 err = kernel_bind(sock, (struct sockaddr *) &myaddr,
1329 sizeof(myaddr)); 1347 sizeof(myaddr));
1330 if (!transport->xprt.resvport) 1348 if (port == 0)
1331 break; 1349 break;
1332 if (err == 0) { 1350 if (err == 0) {
1333 transport->port = port; 1351 transport->port = port;
1334 break; 1352 break;
1335 } 1353 }
1336 if (port <= xprt_min_resvport) 1354 last = port;
1337 port = xprt_max_resvport; 1355 port = xs_next_srcport(transport, sock, port);
1338 else 1356 if (port > last)
1339 port--; 1357 nloop++;
1340 } while (err == -EADDRINUSE && port != transport->port); 1358 } while (err == -EADDRINUSE && nloop != 2);
1341 dprintk("RPC: xs_bind6 "NIP6_FMT":%u: %s (%d)\n", 1359 dprintk("RPC: xs_bind6 "NIP6_FMT":%u: %s (%d)\n",
1342 NIP6(myaddr.sin6_addr), port, err ? "failed" : "ok", err); 1360 NIP6(myaddr.sin6_addr), port, err ? "failed" : "ok", err);
1343 return err; 1361 return err;
@@ -1819,7 +1837,6 @@ static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
1819 xprt->addrlen = args->addrlen; 1837 xprt->addrlen = args->addrlen;
1820 if (args->srcaddr) 1838 if (args->srcaddr)
1821 memcpy(&new->addr, args->srcaddr, args->addrlen); 1839 memcpy(&new->addr, args->srcaddr, args->addrlen);
1822 new->port = xs_get_random_port();
1823 1840
1824 return xprt; 1841 return xprt;
1825} 1842}