diff options
-rw-r--r-- | net/tipc/socket.c | 158 |
1 files changed, 93 insertions, 65 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index d16a6de32ea1..dbce2745f0a8 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c | |||
@@ -775,16 +775,19 @@ exit: | |||
775 | static int auto_connect(struct socket *sock, struct tipc_msg *msg) | 775 | static int auto_connect(struct socket *sock, struct tipc_msg *msg) |
776 | { | 776 | { |
777 | struct tipc_sock *tsock = tipc_sk(sock->sk); | 777 | struct tipc_sock *tsock = tipc_sk(sock->sk); |
778 | 778 | struct tipc_port *p_ptr; | |
779 | if (msg_errcode(msg)) { | ||
780 | sock->state = SS_DISCONNECTING; | ||
781 | return -ECONNREFUSED; | ||
782 | } | ||
783 | 779 | ||
784 | tsock->peer_name.ref = msg_origport(msg); | 780 | tsock->peer_name.ref = msg_origport(msg); |
785 | tsock->peer_name.node = msg_orignode(msg); | 781 | tsock->peer_name.node = msg_orignode(msg); |
786 | tipc_connect(tsock->p->ref, &tsock->peer_name); | 782 | p_ptr = tipc_port_deref(tsock->p->ref); |
787 | tipc_set_portimportance(tsock->p->ref, msg_importance(msg)); | 783 | if (!p_ptr) |
784 | return -EINVAL; | ||
785 | |||
786 | __tipc_connect(tsock->p->ref, p_ptr, &tsock->peer_name); | ||
787 | |||
788 | if (msg_importance(msg) > TIPC_CRITICAL_IMPORTANCE) | ||
789 | return -EINVAL; | ||
790 | msg_set_importance(&p_ptr->phdr, (u32)msg_importance(msg)); | ||
788 | sock->state = SS_CONNECTED; | 791 | sock->state = SS_CONNECTED; |
789 | return 0; | 792 | return 0; |
790 | } | 793 | } |
@@ -1198,7 +1201,9 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf) | |||
1198 | { | 1201 | { |
1199 | struct socket *sock = tsock->sk.sk_socket; | 1202 | struct socket *sock = tsock->sk.sk_socket; |
1200 | struct tipc_msg *msg = buf_msg(*buf); | 1203 | struct tipc_msg *msg = buf_msg(*buf); |
1204 | struct sock *sk = &tsock->sk; | ||
1201 | u32 retval = TIPC_ERR_NO_PORT; | 1205 | u32 retval = TIPC_ERR_NO_PORT; |
1206 | int res; | ||
1202 | 1207 | ||
1203 | if (msg_mcast(msg)) | 1208 | if (msg_mcast(msg)) |
1204 | return retval; | 1209 | return retval; |
@@ -1216,8 +1221,36 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf) | |||
1216 | break; | 1221 | break; |
1217 | case SS_CONNECTING: | 1222 | case SS_CONNECTING: |
1218 | /* Accept only ACK or NACK message */ | 1223 | /* Accept only ACK or NACK message */ |
1219 | if (msg_connected(msg) || (msg_errcode(msg))) | 1224 | if (unlikely(msg_errcode(msg))) { |
1225 | sock->state = SS_DISCONNECTING; | ||
1226 | sk->sk_err = -ECONNREFUSED; | ||
1227 | retval = TIPC_OK; | ||
1228 | break; | ||
1229 | } | ||
1230 | |||
1231 | if (unlikely(!msg_connected(msg))) | ||
1232 | break; | ||
1233 | |||
1234 | res = auto_connect(sock, msg); | ||
1235 | if (res) { | ||
1236 | sock->state = SS_DISCONNECTING; | ||
1237 | sk->sk_err = res; | ||
1220 | retval = TIPC_OK; | 1238 | retval = TIPC_OK; |
1239 | break; | ||
1240 | } | ||
1241 | |||
1242 | /* If an incoming message is an 'ACK-', it should be | ||
1243 | * discarded here because it doesn't contain useful | ||
1244 | * data. In addition, we should try to wake up | ||
1245 | * connect() routine if sleeping. | ||
1246 | */ | ||
1247 | if (msg_data_sz(msg) == 0) { | ||
1248 | kfree_skb(*buf); | ||
1249 | *buf = NULL; | ||
1250 | if (waitqueue_active(sk_sleep(sk))) | ||
1251 | wake_up_interruptible(sk_sleep(sk)); | ||
1252 | } | ||
1253 | retval = TIPC_OK; | ||
1221 | break; | 1254 | break; |
1222 | case SS_LISTENING: | 1255 | case SS_LISTENING: |
1223 | case SS_UNCONNECTED: | 1256 | case SS_UNCONNECTED: |
@@ -1361,8 +1394,6 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, | |||
1361 | struct sock *sk = sock->sk; | 1394 | struct sock *sk = sock->sk; |
1362 | struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; | 1395 | struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; |
1363 | struct msghdr m = {NULL,}; | 1396 | struct msghdr m = {NULL,}; |
1364 | struct sk_buff *buf; | ||
1365 | struct tipc_msg *msg; | ||
1366 | unsigned int timeout; | 1397 | unsigned int timeout; |
1367 | int res; | 1398 | int res; |
1368 | 1399 | ||
@@ -1374,26 +1405,6 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, | |||
1374 | goto exit; | 1405 | goto exit; |
1375 | } | 1406 | } |
1376 | 1407 | ||
1377 | /* For now, TIPC does not support the non-blocking form of connect() */ | ||
1378 | if (flags & O_NONBLOCK) { | ||
1379 | res = -EOPNOTSUPP; | ||
1380 | goto exit; | ||
1381 | } | ||
1382 | |||
1383 | /* Issue Posix-compliant error code if socket is in the wrong state */ | ||
1384 | if (sock->state == SS_LISTENING) { | ||
1385 | res = -EOPNOTSUPP; | ||
1386 | goto exit; | ||
1387 | } | ||
1388 | if (sock->state == SS_CONNECTING) { | ||
1389 | res = -EALREADY; | ||
1390 | goto exit; | ||
1391 | } | ||
1392 | if (sock->state != SS_UNCONNECTED) { | ||
1393 | res = -EISCONN; | ||
1394 | goto exit; | ||
1395 | } | ||
1396 | |||
1397 | /* | 1408 | /* |
1398 | * Reject connection attempt using multicast address | 1409 | * Reject connection attempt using multicast address |
1399 | * | 1410 | * |
@@ -1405,49 +1416,66 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen, | |||
1405 | goto exit; | 1416 | goto exit; |
1406 | } | 1417 | } |
1407 | 1418 | ||
1408 | /* Reject any messages already in receive queue (very unlikely) */ | 1419 | timeout = (flags & O_NONBLOCK) ? 0 : tipc_sk(sk)->conn_timeout; |
1409 | reject_rx_queue(sk); | 1420 | |
1421 | switch (sock->state) { | ||
1422 | case SS_UNCONNECTED: | ||
1423 | /* Send a 'SYN-' to destination */ | ||
1424 | m.msg_name = dest; | ||
1425 | m.msg_namelen = destlen; | ||
1426 | |||
1427 | /* If connect is in non-blocking case, set MSG_DONTWAIT to | ||
1428 | * indicate send_msg() is never blocked. | ||
1429 | */ | ||
1430 | if (!timeout) | ||
1431 | m.msg_flags = MSG_DONTWAIT; | ||
1432 | |||
1433 | res = send_msg(NULL, sock, &m, 0); | ||
1434 | if ((res < 0) && (res != -EWOULDBLOCK)) | ||
1435 | goto exit; | ||
1410 | 1436 | ||
1411 | /* Send a 'SYN-' to destination */ | 1437 | /* Just entered SS_CONNECTING state; the only |
1412 | m.msg_name = dest; | 1438 | * difference is that return value in non-blocking |
1413 | m.msg_namelen = destlen; | 1439 | * case is EINPROGRESS, rather than EALREADY. |
1414 | res = send_msg(NULL, sock, &m, 0); | 1440 | */ |
1415 | if (res < 0) | 1441 | res = -EINPROGRESS; |
1442 | break; | ||
1443 | case SS_CONNECTING: | ||
1444 | res = -EALREADY; | ||
1445 | break; | ||
1446 | case SS_CONNECTED: | ||
1447 | res = -EISCONN; | ||
1448 | break; | ||
1449 | default: | ||
1450 | res = -EINVAL; | ||
1416 | goto exit; | 1451 | goto exit; |
1452 | } | ||
1417 | 1453 | ||
1418 | /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ | 1454 | if (sock->state == SS_CONNECTING) { |
1419 | timeout = tipc_sk(sk)->conn_timeout; | 1455 | if (!timeout) |
1420 | release_sock(sk); | 1456 | goto exit; |
1421 | res = wait_event_interruptible_timeout(*sk_sleep(sk), | ||
1422 | (!skb_queue_empty(&sk->sk_receive_queue) || | ||
1423 | (sock->state != SS_CONNECTING)), | ||
1424 | timeout ? (long)msecs_to_jiffies(timeout) | ||
1425 | : MAX_SCHEDULE_TIMEOUT); | ||
1426 | lock_sock(sk); | ||
1427 | 1457 | ||
1428 | if (res > 0) { | 1458 | /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ |
1429 | buf = skb_peek(&sk->sk_receive_queue); | 1459 | release_sock(sk); |
1430 | if (buf != NULL) { | 1460 | res = wait_event_interruptible_timeout(*sk_sleep(sk), |
1431 | msg = buf_msg(buf); | 1461 | sock->state != SS_CONNECTING, |
1432 | res = auto_connect(sock, msg); | 1462 | timeout ? (long)msecs_to_jiffies(timeout) |
1433 | if (!res) { | 1463 | : MAX_SCHEDULE_TIMEOUT); |
1434 | if (!msg_data_sz(msg)) | 1464 | lock_sock(sk); |
1435 | advance_rx_queue(sk); | 1465 | if (res <= 0) { |
1436 | } | 1466 | if (res == 0) |
1437 | } else { | 1467 | res = -ETIMEDOUT; |
1438 | if (sock->state == SS_CONNECTED) | ||
1439 | res = -EISCONN; | ||
1440 | else | 1468 | else |
1441 | res = -ECONNREFUSED; | 1469 | ; /* leave "res" unchanged */ |
1470 | goto exit; | ||
1442 | } | 1471 | } |
1443 | } else { | ||
1444 | if (res == 0) | ||
1445 | res = -ETIMEDOUT; | ||
1446 | else | ||
1447 | ; /* leave "res" unchanged */ | ||
1448 | sock->state = SS_DISCONNECTING; | ||
1449 | } | 1472 | } |
1450 | 1473 | ||
1474 | if (unlikely(sock->state == SS_DISCONNECTING)) | ||
1475 | res = sock_error(sk); | ||
1476 | else | ||
1477 | res = 0; | ||
1478 | |||
1451 | exit: | 1479 | exit: |
1452 | release_sock(sk); | 1480 | release_sock(sk); |
1453 | return res; | 1481 | return res; |