18c3e34a4SDavid Howells /* RxRPC packet transmission 28c3e34a4SDavid Howells * 38c3e34a4SDavid Howells * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 48c3e34a4SDavid Howells * Written by David Howells (dhowells@redhat.com) 58c3e34a4SDavid Howells * 68c3e34a4SDavid Howells * This program is free software; you can redistribute it and/or 78c3e34a4SDavid Howells * modify it under the terms of the GNU General Public License 88c3e34a4SDavid Howells * as published by the Free Software Foundation; either version 98c3e34a4SDavid Howells * 2 of the License, or (at your option) any later version. 108c3e34a4SDavid Howells */ 118c3e34a4SDavid Howells 128c3e34a4SDavid Howells #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c3e34a4SDavid Howells 148c3e34a4SDavid Howells #include <linux/net.h> 158c3e34a4SDavid Howells #include <linux/gfp.h> 168c3e34a4SDavid Howells #include <linux/skbuff.h> 178c3e34a4SDavid Howells #include <linux/export.h> 188c3e34a4SDavid Howells #include <net/sock.h> 198c3e34a4SDavid Howells #include <net/af_rxrpc.h> 208c3e34a4SDavid Howells #include "ar-internal.h" 218c3e34a4SDavid Howells 228d94aa38SDavid Howells struct rxrpc_pkt_buffer { 238d94aa38SDavid Howells struct rxrpc_wire_header whdr; 248d94aa38SDavid Howells union { 258d94aa38SDavid Howells struct { 268d94aa38SDavid Howells struct rxrpc_ackpacket ack; 278d94aa38SDavid Howells u8 acks[255]; 288d94aa38SDavid Howells u8 pad[3]; 298d94aa38SDavid Howells }; 308d94aa38SDavid Howells __be32 abort_code; 318d94aa38SDavid Howells }; 328d94aa38SDavid Howells struct rxrpc_ackinfo ackinfo; 338d94aa38SDavid Howells }; 348d94aa38SDavid Howells 358d94aa38SDavid Howells /* 368d94aa38SDavid Howells * Fill out an ACK packet. 378d94aa38SDavid Howells */ 388d94aa38SDavid Howells static size_t rxrpc_fill_out_ack(struct rxrpc_call *call, 398d94aa38SDavid Howells struct rxrpc_pkt_buffer *pkt) 408d94aa38SDavid Howells { 41248f219cSDavid Howells rxrpc_seq_t hard_ack, top, seq; 42248f219cSDavid Howells int ix; 438d94aa38SDavid Howells u32 mtu, jmax; 448d94aa38SDavid Howells u8 *ackp = pkt->acks; 458d94aa38SDavid Howells 46248f219cSDavid Howells /* Barrier against rxrpc_input_data(). */ 47248f219cSDavid Howells hard_ack = READ_ONCE(call->rx_hard_ack); 48248f219cSDavid Howells top = smp_load_acquire(&call->rx_top); 49248f219cSDavid Howells 508d94aa38SDavid Howells pkt->ack.bufferSpace = htons(8); 51248f219cSDavid Howells pkt->ack.maxSkew = htons(call->ackr_skew); 52248f219cSDavid Howells pkt->ack.firstPacket = htonl(hard_ack + 1); 538d94aa38SDavid Howells pkt->ack.previousPacket = htonl(call->ackr_prev_seq); 548d94aa38SDavid Howells pkt->ack.serial = htonl(call->ackr_serial); 55248f219cSDavid Howells pkt->ack.reason = call->ackr_reason; 56248f219cSDavid Howells pkt->ack.nAcks = top - hard_ack; 578d94aa38SDavid Howells 58248f219cSDavid Howells if (after(top, hard_ack)) { 59248f219cSDavid Howells seq = hard_ack + 1; 60248f219cSDavid Howells do { 61248f219cSDavid Howells ix = seq & RXRPC_RXTX_BUFF_MASK; 62248f219cSDavid Howells if (call->rxtx_buffer[ix]) 63248f219cSDavid Howells *ackp++ = RXRPC_ACK_TYPE_ACK; 64248f219cSDavid Howells else 65248f219cSDavid Howells *ackp++ = RXRPC_ACK_TYPE_NACK; 66248f219cSDavid Howells seq++; 67248f219cSDavid Howells } while (before_eq(seq, top)); 68248f219cSDavid Howells } 69248f219cSDavid Howells 70248f219cSDavid Howells mtu = call->conn->params.peer->if_mtu; 71248f219cSDavid Howells mtu -= call->conn->params.peer->hdrsize; 7275e42126SDavid Howells jmax = (call->nr_jumbo_bad > 3) ? 1 : rxrpc_rx_jumbo_max; 738d94aa38SDavid Howells pkt->ackinfo.rxMTU = htonl(rxrpc_rx_mtu); 748d94aa38SDavid Howells pkt->ackinfo.maxMTU = htonl(mtu); 7575e42126SDavid Howells pkt->ackinfo.rwind = htonl(call->rx_winsize); 768d94aa38SDavid Howells pkt->ackinfo.jumbo_max = htonl(jmax); 778d94aa38SDavid Howells 788d94aa38SDavid Howells *ackp++ = 0; 798d94aa38SDavid Howells *ackp++ = 0; 808d94aa38SDavid Howells *ackp++ = 0; 81248f219cSDavid Howells return top - hard_ack + 3; 828d94aa38SDavid Howells } 838d94aa38SDavid Howells 848d94aa38SDavid Howells /* 85248f219cSDavid Howells * Send an ACK or ABORT call packet. 868d94aa38SDavid Howells */ 878d94aa38SDavid Howells int rxrpc_send_call_packet(struct rxrpc_call *call, u8 type) 888d94aa38SDavid Howells { 898d94aa38SDavid Howells struct rxrpc_connection *conn = NULL; 908d94aa38SDavid Howells struct rxrpc_pkt_buffer *pkt; 918d94aa38SDavid Howells struct msghdr msg; 928d94aa38SDavid Howells struct kvec iov[2]; 938d94aa38SDavid Howells rxrpc_serial_t serial; 948d94aa38SDavid Howells size_t len, n; 958d94aa38SDavid Howells int ioc, ret; 968d94aa38SDavid Howells u32 abort_code; 978d94aa38SDavid Howells 988d94aa38SDavid Howells _enter("%u,%s", call->debug_id, rxrpc_pkts[type]); 998d94aa38SDavid Howells 1008d94aa38SDavid Howells spin_lock_bh(&call->lock); 1018d94aa38SDavid Howells if (call->conn) 1028d94aa38SDavid Howells conn = rxrpc_get_connection_maybe(call->conn); 1038d94aa38SDavid Howells spin_unlock_bh(&call->lock); 1048d94aa38SDavid Howells if (!conn) 1058d94aa38SDavid Howells return -ECONNRESET; 1068d94aa38SDavid Howells 1078d94aa38SDavid Howells pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); 1088d94aa38SDavid Howells if (!pkt) { 1098d94aa38SDavid Howells rxrpc_put_connection(conn); 1108d94aa38SDavid Howells return -ENOMEM; 1118d94aa38SDavid Howells } 1128d94aa38SDavid Howells 1138d94aa38SDavid Howells serial = atomic_inc_return(&conn->serial); 1148d94aa38SDavid Howells 1158d94aa38SDavid Howells msg.msg_name = &call->peer->srx.transport; 1168d94aa38SDavid Howells msg.msg_namelen = call->peer->srx.transport_len; 1178d94aa38SDavid Howells msg.msg_control = NULL; 1188d94aa38SDavid Howells msg.msg_controllen = 0; 1198d94aa38SDavid Howells msg.msg_flags = 0; 1208d94aa38SDavid Howells 1218d94aa38SDavid Howells pkt->whdr.epoch = htonl(conn->proto.epoch); 1228d94aa38SDavid Howells pkt->whdr.cid = htonl(call->cid); 1238d94aa38SDavid Howells pkt->whdr.callNumber = htonl(call->call_id); 1248d94aa38SDavid Howells pkt->whdr.seq = 0; 1258d94aa38SDavid Howells pkt->whdr.serial = htonl(serial); 1268d94aa38SDavid Howells pkt->whdr.type = type; 1278d94aa38SDavid Howells pkt->whdr.flags = conn->out_clientflag; 1288d94aa38SDavid Howells pkt->whdr.userStatus = 0; 1298d94aa38SDavid Howells pkt->whdr.securityIndex = call->security_ix; 1308d94aa38SDavid Howells pkt->whdr._rsvd = 0; 1318d94aa38SDavid Howells pkt->whdr.serviceId = htons(call->service_id); 1328d94aa38SDavid Howells 1338d94aa38SDavid Howells iov[0].iov_base = pkt; 1348d94aa38SDavid Howells iov[0].iov_len = sizeof(pkt->whdr); 1358d94aa38SDavid Howells len = sizeof(pkt->whdr); 1368d94aa38SDavid Howells 1378d94aa38SDavid Howells switch (type) { 1388d94aa38SDavid Howells case RXRPC_PACKET_TYPE_ACK: 1398d94aa38SDavid Howells spin_lock_bh(&call->lock); 14027d0fc43SDavid Howells if (!call->ackr_reason) { 14127d0fc43SDavid Howells spin_unlock_bh(&call->lock); 14227d0fc43SDavid Howells ret = 0; 14327d0fc43SDavid Howells goto out; 14427d0fc43SDavid Howells } 1458d94aa38SDavid Howells n = rxrpc_fill_out_ack(call, pkt); 1468d94aa38SDavid Howells call->ackr_reason = 0; 1478d94aa38SDavid Howells 1488d94aa38SDavid Howells spin_unlock_bh(&call->lock); 1498d94aa38SDavid Howells 1508d94aa38SDavid Howells _proto("Tx ACK %%%u { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", 1518d94aa38SDavid Howells serial, 1528d94aa38SDavid Howells ntohs(pkt->ack.maxSkew), 1538d94aa38SDavid Howells ntohl(pkt->ack.firstPacket), 1548d94aa38SDavid Howells ntohl(pkt->ack.previousPacket), 1558d94aa38SDavid Howells ntohl(pkt->ack.serial), 1568d94aa38SDavid Howells rxrpc_acks(pkt->ack.reason), 1578d94aa38SDavid Howells pkt->ack.nAcks); 1588d94aa38SDavid Howells 1598d94aa38SDavid Howells iov[0].iov_len += sizeof(pkt->ack) + n; 1608d94aa38SDavid Howells iov[1].iov_base = &pkt->ackinfo; 1618d94aa38SDavid Howells iov[1].iov_len = sizeof(pkt->ackinfo); 1628d94aa38SDavid Howells len += sizeof(pkt->ack) + n + sizeof(pkt->ackinfo); 1638d94aa38SDavid Howells ioc = 2; 1648d94aa38SDavid Howells break; 1658d94aa38SDavid Howells 1668d94aa38SDavid Howells case RXRPC_PACKET_TYPE_ABORT: 1678d94aa38SDavid Howells abort_code = call->abort_code; 1688d94aa38SDavid Howells pkt->abort_code = htonl(abort_code); 1698d94aa38SDavid Howells _proto("Tx ABORT %%%u { %d }", serial, abort_code); 1708d94aa38SDavid Howells iov[0].iov_len += sizeof(pkt->abort_code); 1718d94aa38SDavid Howells len += sizeof(pkt->abort_code); 1728d94aa38SDavid Howells ioc = 1; 1738d94aa38SDavid Howells break; 1748d94aa38SDavid Howells 1758d94aa38SDavid Howells default: 1768d94aa38SDavid Howells BUG(); 1778d94aa38SDavid Howells ret = -ENOANO; 1788d94aa38SDavid Howells goto out; 1798d94aa38SDavid Howells } 1808d94aa38SDavid Howells 1818d94aa38SDavid Howells ret = kernel_sendmsg(conn->params.local->socket, 1828d94aa38SDavid Howells &msg, iov, ioc, len); 1838d94aa38SDavid Howells 184248f219cSDavid Howells if (ret < 0 && call->state < RXRPC_CALL_COMPLETE) { 1852311e327SDavid Howells switch (type) { 186248f219cSDavid Howells case RXRPC_PACKET_TYPE_ACK: 187248f219cSDavid Howells rxrpc_propose_ACK(call, pkt->ack.reason, 188248f219cSDavid Howells ntohs(pkt->ack.maxSkew), 189248f219cSDavid Howells ntohl(pkt->ack.serial), 190248f219cSDavid Howells true, true); 191248f219cSDavid Howells break; 192248f219cSDavid Howells case RXRPC_PACKET_TYPE_ABORT: 193248f219cSDavid Howells break; 194248f219cSDavid Howells } 195248f219cSDavid Howells } 196248f219cSDavid Howells 1978d94aa38SDavid Howells out: 1988d94aa38SDavid Howells rxrpc_put_connection(conn); 1998d94aa38SDavid Howells kfree(pkt); 2008d94aa38SDavid Howells return ret; 2018d94aa38SDavid Howells } 2028d94aa38SDavid Howells 2038c3e34a4SDavid Howells /* 2048c3e34a4SDavid Howells * send a packet through the transport endpoint 2058c3e34a4SDavid Howells */ 206985a5c82SDavid Howells int rxrpc_send_data_packet(struct rxrpc_connection *conn, struct sk_buff *skb) 2078c3e34a4SDavid Howells { 2088c3e34a4SDavid Howells struct kvec iov[1]; 2098c3e34a4SDavid Howells struct msghdr msg; 2108c3e34a4SDavid Howells int ret, opt; 2118c3e34a4SDavid Howells 2128c3e34a4SDavid Howells _enter(",{%d}", skb->len); 2138c3e34a4SDavid Howells 2148c3e34a4SDavid Howells iov[0].iov_base = skb->head; 2158c3e34a4SDavid Howells iov[0].iov_len = skb->len; 2168c3e34a4SDavid Howells 217985a5c82SDavid Howells msg.msg_name = &conn->params.peer->srx.transport; 218985a5c82SDavid Howells msg.msg_namelen = conn->params.peer->srx.transport_len; 2198c3e34a4SDavid Howells msg.msg_control = NULL; 2208c3e34a4SDavid Howells msg.msg_controllen = 0; 2218c3e34a4SDavid Howells msg.msg_flags = 0; 2228c3e34a4SDavid Howells 2238c3e34a4SDavid Howells /* send the packet with the don't fragment bit set if we currently 2248c3e34a4SDavid Howells * think it's small enough */ 225985a5c82SDavid Howells if (skb->len - sizeof(struct rxrpc_wire_header) < conn->params.peer->maxdata) { 226985a5c82SDavid Howells down_read(&conn->params.local->defrag_sem); 2278c3e34a4SDavid Howells /* send the packet by UDP 2288c3e34a4SDavid Howells * - returns -EMSGSIZE if UDP would have to fragment the packet 2298c3e34a4SDavid Howells * to go out of the interface 2308c3e34a4SDavid Howells * - in which case, we'll have processed the ICMP error 2318c3e34a4SDavid Howells * message and update the peer record 2328c3e34a4SDavid Howells */ 233985a5c82SDavid Howells ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 1, 2348c3e34a4SDavid Howells iov[0].iov_len); 2358c3e34a4SDavid Howells 236985a5c82SDavid Howells up_read(&conn->params.local->defrag_sem); 2378c3e34a4SDavid Howells if (ret == -EMSGSIZE) 2388c3e34a4SDavid Howells goto send_fragmentable; 2398c3e34a4SDavid Howells 240985a5c82SDavid Howells _leave(" = %d [%u]", ret, conn->params.peer->maxdata); 2418c3e34a4SDavid Howells return ret; 2428c3e34a4SDavid Howells } 2438c3e34a4SDavid Howells 2448c3e34a4SDavid Howells send_fragmentable: 2458c3e34a4SDavid Howells /* attempt to send this message with fragmentation enabled */ 2468c3e34a4SDavid Howells _debug("send fragment"); 2478c3e34a4SDavid Howells 248985a5c82SDavid Howells down_write(&conn->params.local->defrag_sem); 249985a5c82SDavid Howells 250985a5c82SDavid Howells switch (conn->params.local->srx.transport.family) { 251985a5c82SDavid Howells case AF_INET: 2528c3e34a4SDavid Howells opt = IP_PMTUDISC_DONT; 253985a5c82SDavid Howells ret = kernel_setsockopt(conn->params.local->socket, 254985a5c82SDavid Howells SOL_IP, IP_MTU_DISCOVER, 2558c3e34a4SDavid Howells (char *)&opt, sizeof(opt)); 2568c3e34a4SDavid Howells if (ret == 0) { 257985a5c82SDavid Howells ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 1, 2588c3e34a4SDavid Howells iov[0].iov_len); 2598c3e34a4SDavid Howells 2608c3e34a4SDavid Howells opt = IP_PMTUDISC_DO; 261985a5c82SDavid Howells kernel_setsockopt(conn->params.local->socket, SOL_IP, 262985a5c82SDavid Howells IP_MTU_DISCOVER, 263985a5c82SDavid Howells (char *)&opt, sizeof(opt)); 264985a5c82SDavid Howells } 265985a5c82SDavid Howells break; 26675b54cb5SDavid Howells 267d1912747SDavid Howells #ifdef CONFIG_AF_RXRPC_IPV6 26875b54cb5SDavid Howells case AF_INET6: 26975b54cb5SDavid Howells opt = IPV6_PMTUDISC_DONT; 27075b54cb5SDavid Howells ret = kernel_setsockopt(conn->params.local->socket, 27175b54cb5SDavid Howells SOL_IPV6, IPV6_MTU_DISCOVER, 27275b54cb5SDavid Howells (char *)&opt, sizeof(opt)); 27375b54cb5SDavid Howells if (ret == 0) { 27475b54cb5SDavid Howells ret = kernel_sendmsg(conn->params.local->socket, &msg, 27575b54cb5SDavid Howells iov, 1, iov[0].iov_len); 27675b54cb5SDavid Howells 27775b54cb5SDavid Howells opt = IPV6_PMTUDISC_DO; 27875b54cb5SDavid Howells kernel_setsockopt(conn->params.local->socket, 27975b54cb5SDavid Howells SOL_IPV6, IPV6_MTU_DISCOVER, 28075b54cb5SDavid Howells (char *)&opt, sizeof(opt)); 28175b54cb5SDavid Howells } 28275b54cb5SDavid Howells break; 283d1912747SDavid Howells #endif 2848c3e34a4SDavid Howells } 2858c3e34a4SDavid Howells 286985a5c82SDavid Howells up_write(&conn->params.local->defrag_sem); 287985a5c82SDavid Howells _leave(" = %d [frag %u]", ret, conn->params.peer->maxdata); 2888c3e34a4SDavid Howells return ret; 2898c3e34a4SDavid Howells } 290248f219cSDavid Howells 291248f219cSDavid Howells /* 292248f219cSDavid Howells * reject packets through the local endpoint 293248f219cSDavid Howells */ 294248f219cSDavid Howells void rxrpc_reject_packets(struct rxrpc_local *local) 295248f219cSDavid Howells { 2961c2bc7b9SDavid Howells struct sockaddr_rxrpc srx; 297248f219cSDavid Howells struct rxrpc_skb_priv *sp; 298248f219cSDavid Howells struct rxrpc_wire_header whdr; 299248f219cSDavid Howells struct sk_buff *skb; 300248f219cSDavid Howells struct msghdr msg; 301248f219cSDavid Howells struct kvec iov[2]; 302248f219cSDavid Howells size_t size; 303248f219cSDavid Howells __be32 code; 304248f219cSDavid Howells 305248f219cSDavid Howells _enter("%d", local->debug_id); 306248f219cSDavid Howells 307248f219cSDavid Howells iov[0].iov_base = &whdr; 308248f219cSDavid Howells iov[0].iov_len = sizeof(whdr); 309248f219cSDavid Howells iov[1].iov_base = &code; 310248f219cSDavid Howells iov[1].iov_len = sizeof(code); 311248f219cSDavid Howells size = sizeof(whdr) + sizeof(code); 312248f219cSDavid Howells 3131c2bc7b9SDavid Howells msg.msg_name = &srx.transport; 314248f219cSDavid Howells msg.msg_control = NULL; 315248f219cSDavid Howells msg.msg_controllen = 0; 316248f219cSDavid Howells msg.msg_flags = 0; 317248f219cSDavid Howells 318248f219cSDavid Howells memset(&whdr, 0, sizeof(whdr)); 319248f219cSDavid Howells whdr.type = RXRPC_PACKET_TYPE_ABORT; 320248f219cSDavid Howells 321248f219cSDavid Howells while ((skb = skb_dequeue(&local->reject_queue))) { 322248f219cSDavid Howells rxrpc_see_skb(skb); 323248f219cSDavid Howells sp = rxrpc_skb(skb); 3241c2bc7b9SDavid Howells 3251c2bc7b9SDavid Howells if (rxrpc_extract_addr_from_skb(&srx, skb) == 0) { 3261c2bc7b9SDavid Howells msg.msg_namelen = srx.transport_len; 3271c2bc7b9SDavid Howells 328248f219cSDavid Howells code = htonl(skb->priority); 329248f219cSDavid Howells 330248f219cSDavid Howells whdr.epoch = htonl(sp->hdr.epoch); 331248f219cSDavid Howells whdr.cid = htonl(sp->hdr.cid); 332248f219cSDavid Howells whdr.callNumber = htonl(sp->hdr.callNumber); 333248f219cSDavid Howells whdr.serviceId = htons(sp->hdr.serviceId); 334248f219cSDavid Howells whdr.flags = sp->hdr.flags; 335248f219cSDavid Howells whdr.flags ^= RXRPC_CLIENT_INITIATED; 336248f219cSDavid Howells whdr.flags &= RXRPC_CLIENT_INITIATED; 337248f219cSDavid Howells 338248f219cSDavid Howells kernel_sendmsg(local->socket, &msg, iov, 2, size); 339248f219cSDavid Howells } 340248f219cSDavid Howells 341248f219cSDavid Howells rxrpc_free_skb(skb); 342248f219cSDavid Howells } 343248f219cSDavid Howells 344248f219cSDavid Howells _leave(""); 345248f219cSDavid Howells } 346