#include <net/sock.h>
#include <net/checksum.h>
#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/tcp_states.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/stats.h>
}
#endif
+static char *__svc_print_addr(struct sockaddr *addr, char *buf, size_t len)
+{
+ switch (addr->sa_family) {
+ case AF_INET:
+ snprintf(buf, len, "%u.%u.%u.%u, port=%u",
+ NIPQUAD(((struct sockaddr_in *) addr)->sin_addr),
+ htons(((struct sockaddr_in *) addr)->sin_port));
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ snprintf(buf, len, "%x:%x:%x:%x:%x:%x:%x:%x, port=%u",
+ NIP6(((struct sockaddr_in6 *) addr)->sin6_addr),
+ htons(((struct sockaddr_in6 *) addr)->sin6_port));
+ break;
+#endif
+ default:
+ snprintf(buf, len, "unknown address type: %d", addr->sa_family);
+ break;
+ }
+ return buf;
+}
+
+/**
+ * svc_print_addr - Format rq_addr field for printing
+ * @rqstp: svc_rqst struct containing address to print
+ * @buf: target buffer for formatted address
+ * @len: length of target buffer
+ *
+ */
+char *svc_print_addr(struct svc_rqst *rqstp, char *buf, size_t len)
+{
+ return __svc_print_addr(svc_addr(rqstp), buf, len);
+}
+EXPORT_SYMBOL_GPL(svc_print_addr);
+
/*
* Queue up an idle server thread. Must have pool->sp_lock held.
* Note: this is really a stack rather than a queue, so that we only
}
}
+union svc_pktinfo_u {
+ struct in_pktinfo pkti;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct in6_pktinfo pkti6;
+#endif
+};
+
+static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh)
+{
+ switch (rqstp->rq_sock->sk_sk->sk_family) {
+ case AF_INET: {
+ struct in_pktinfo *pki = CMSG_DATA(cmh);
+
+ cmh->cmsg_level = SOL_IP;
+ cmh->cmsg_type = IP_PKTINFO;
+ pki->ipi_ifindex = 0;
+ pki->ipi_spec_dst.s_addr = rqstp->rq_daddr.addr.s_addr;
+ cmh->cmsg_len = CMSG_LEN(sizeof(*pki));
+ }
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6: {
+ struct in6_pktinfo *pki = CMSG_DATA(cmh);
+
+ cmh->cmsg_level = SOL_IPV6;
+ cmh->cmsg_type = IPV6_PKTINFO;
+ pki->ipi6_ifindex = 0;
+ ipv6_addr_copy(&pki->ipi6_addr,
+ &rqstp->rq_daddr.addr6);
+ cmh->cmsg_len = CMSG_LEN(sizeof(*pki));
+ }
+ break;
+#endif
+ }
+ return;
+}
+
/*
* Generic sendto routine
*/
struct svc_sock *svsk = rqstp->rq_sock;
struct socket *sock = svsk->sk_sock;
int slen;
- char buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
+ char buffer[CMSG_SPACE(sizeof(union svc_pktinfo_u))];
struct cmsghdr *cmh = (struct cmsghdr *)buffer;
- struct in_pktinfo *pki = (struct in_pktinfo *)CMSG_DATA(cmh);
int len = 0;
int result;
int size;
size_t base = xdr->page_base;
unsigned int pglen = xdr->page_len;
unsigned int flags = MSG_MORE;
+ char buf[RPC_MAX_ADDRBUFLEN];
slen = xdr->len;
if (rqstp->rq_prot == IPPROTO_UDP) {
- /* set the source and destination */
- struct msghdr msg;
- msg.msg_name = &rqstp->rq_addr;
- msg.msg_namelen = sizeof(rqstp->rq_addr);
- msg.msg_iov = NULL;
- msg.msg_iovlen = 0;
- msg.msg_flags = MSG_MORE;
-
- msg.msg_control = cmh;
- msg.msg_controllen = sizeof(buffer);
- cmh->cmsg_len = CMSG_LEN(sizeof(*pki));
- cmh->cmsg_level = SOL_IP;
- cmh->cmsg_type = IP_PKTINFO;
- pki->ipi_ifindex = 0;
- pki->ipi_spec_dst.s_addr = rqstp->rq_daddr;
+ struct msghdr msg = {
+ .msg_name = &rqstp->rq_addr,
+ .msg_namelen = rqstp->rq_addrlen,
+ .msg_control = cmh,
+ .msg_controllen = sizeof(buffer),
+ .msg_flags = MSG_MORE,
+ };
+
+ svc_set_cmsg_data(rqstp, cmh);
if (sock_sendmsg(sock, &msg, 0) < 0)
goto out;
len += result;
}
out:
- dprintk("svc: socket %p sendto([%p %Zu... ], %d) = %d (addr %x)\n",
- rqstp->rq_sock, xdr->head[0].iov_base, xdr->head[0].iov_len, xdr->len, len,
- rqstp->rq_addr.sin_addr.s_addr);
+ dprintk("svc: socket %p sendto([%p %Zu... ], %d) = %d (addr %s)\n",
+ rqstp->rq_sock, xdr->head[0].iov_base, xdr->head[0].iov_len,
+ xdr->len, len, svc_print_addr(rqstp, buf, sizeof(buf)));
return len;
}
svc_recvfrom(struct svc_rqst *rqstp, struct kvec *iov, int nr, int buflen)
{
struct svc_sock *svsk = rqstp->rq_sock;
- struct msghdr msg;
- struct socket *sock;
- int len;
-
- rqstp->rq_addrlen = sizeof(rqstp->rq_addr);
- sock = svsk->sk_sock;
-
- msg.msg_name = &rqstp->rq_addr;
- msg.msg_namelen = sizeof(rqstp->rq_addr);
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
-
- msg.msg_flags = MSG_DONTWAIT;
+ struct msghdr msg = {
+ .msg_flags = MSG_DONTWAIT,
+ };
+ int len;
- len = kernel_recvmsg(sock, &msg, iov, nr, buflen, MSG_DONTWAIT);
+ len = kernel_recvmsg(svsk->sk_sock, &msg, iov, nr, buflen,
+ msg.msg_flags);
/* sock_recvmsg doesn't fill in the name/namelen, so we must..
*/
rqstp->rq_addrlen = svsk->sk_remotelen;
dprintk("svc: socket %p recvfrom(%p, %Zu) = %d\n",
- rqstp->rq_sock, iov[0].iov_base, iov[0].iov_len, len);
+ svsk, iov[0].iov_base, iov[0].iov_len, len);
return len;
}
}
}
+static void svc_udp_get_sender_address(struct svc_rqst *rqstp,
+ struct sk_buff *skb)
+{
+ switch (rqstp->rq_sock->sk_sk->sk_family) {
+ case AF_INET: {
+ /* this seems to come from net/ipv4/udp.c:udp_recvmsg */
+ struct sockaddr_in *sin = svc_addr_in(rqstp);
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = skb->h.uh->source;
+ sin->sin_addr.s_addr = skb->nh.iph->saddr;
+ rqstp->rq_addrlen = sizeof(struct sockaddr_in);
+ /* Remember which interface received this request */
+ rqstp->rq_daddr.addr.s_addr = skb->nh.iph->daddr;
+ }
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6: {
+ /* this is derived from net/ipv6/udp.c:udpv6_recvmesg */
+ struct sockaddr_in6 *sin6 = svc_addr_in6(rqstp);
+
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = skb->h.uh->source;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_scope_id = 0;
+ if (ipv6_addr_type(&sin6->sin6_addr) &
+ IPV6_ADDR_LINKLOCAL)
+ sin6->sin6_scope_id = IP6CB(skb)->iif;
+ ipv6_addr_copy(&sin6->sin6_addr,
+ &skb->nh.ipv6h->saddr);
+ rqstp->rq_addrlen = sizeof(struct sockaddr_in);
+ /* Remember which interface received this request */
+ ipv6_addr_copy(&rqstp->rq_daddr.addr6,
+ &skb->nh.ipv6h->saddr);
+ }
+ break;
+#endif
+ }
+ return;
+}
+
/*
* Receive a datagram from a UDP socket.
*/
len = skb->len - sizeof(struct udphdr);
rqstp->rq_arg.len = len;
- rqstp->rq_prot = IPPROTO_UDP;
+ rqstp->rq_prot = IPPROTO_UDP;
- /* Get sender address */
- rqstp->rq_addr.sin_family = AF_INET;
- rqstp->rq_addr.sin_port = skb->h.uh->source;
- rqstp->rq_addr.sin_addr.s_addr = skb->nh.iph->saddr;
- rqstp->rq_daddr = skb->nh.iph->daddr;
+ svc_udp_get_sender_address(rqstp, skb);
if (skb_is_nonlinear(skb)) {
/* we have to copy */
wake_up_interruptible(sk->sk_sleep);
}
+static inline int svc_port_is_privileged(struct sockaddr *sin)
+{
+ switch (sin->sa_family) {
+ case AF_INET:
+ return ntohs(((struct sockaddr_in *)sin)->sin_port)
+ < PROT_SOCK;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ return ntohs(((struct sockaddr_in6 *)sin)->sin6_port)
+ < PROT_SOCK;
+#endif
+ default:
+ return 0;
+ }
+}
+
/*
* Accept a TCP connection
*/
static void
svc_tcp_accept(struct svc_sock *svsk)
{
- struct sockaddr_in sin;
+ struct sockaddr_storage addr;
+ struct sockaddr *sin = (struct sockaddr *) &addr;
struct svc_serv *serv = svsk->sk_server;
struct socket *sock = svsk->sk_sock;
struct socket *newsock;
struct svc_sock *newsvsk;
int err, slen;
+ char buf[RPC_MAX_ADDRBUFLEN];
dprintk("svc: tcp_accept %p sock %p\n", svsk, sock);
if (!sock)
set_bit(SK_CONN, &svsk->sk_flags);
svc_sock_enqueue(svsk);
- slen = sizeof(sin);
- err = kernel_getpeername(newsock, (struct sockaddr *) &sin, &slen);
+ err = kernel_getpeername(newsock, sin, &slen);
if (err < 0) {
if (net_ratelimit())
printk(KERN_WARNING "%s: peername failed (err %d)!\n",
}
/* Ideally, we would want to reject connections from unauthorized
- * hosts here, but when we get encription, the IP of the host won't
- * tell us anything. For now just warn about unpriv connections.
+ * hosts here, but when we get encryption, the IP of the host won't
+ * tell us anything. For now just warn about unpriv connections.
*/
- if (ntohs(sin.sin_port) >= 1024) {
+ if (!svc_port_is_privileged(sin)) {
dprintk(KERN_WARNING
- "%s: connect from unprivileged port: %u.%u.%u.%u:%d\n",
+ "%s: connect from unprivileged port: %s\n",
serv->sv_name,
- NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+ __svc_print_addr(sin, buf, sizeof(buf)));
}
-
- dprintk("%s: connect from %u.%u.%u.%u:%04x\n", serv->sv_name,
- NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+ dprintk("%s: connect from %s\n", serv->sv_name,
+ __svc_print_addr(sin, buf, sizeof(buf)));
/* make sure that a write doesn't block forever when
* low on memory
if (!(newsvsk = svc_setup_socket(serv, newsock, &err,
(SVC_SOCK_ANONYMOUS | SVC_SOCK_TEMPORARY))))
goto failed;
- memcpy(&newsvsk->sk_remote, &sin, slen);
+ memcpy(&newsvsk->sk_remote, sin, slen);
newsvsk->sk_remotelen = slen;
svc_sock_received(newsvsk);
"sockets, consider increasing the "
"number of nfsd threads\n",
serv->sv_name);
- printk(KERN_NOTICE "%s: last TCP connect from "
- "%u.%u.%u.%u:%d\n",
- serv->sv_name,
- NIPQUAD(sin.sin_addr.s_addr),
- ntohs(sin.sin_port));
+ printk(KERN_NOTICE
+ "%s: last TCP connect from %s\n",
+ serv->sv_name, buf);
}
/*
* Always select the oldest socket. It's not fair,
int
svc_recv(struct svc_rqst *rqstp, long timeout)
{
- struct svc_sock *svsk =NULL;
+ struct svc_sock *svsk = NULL;
struct svc_serv *serv = rqstp->rq_server;
struct svc_pool *pool = rqstp->rq_pool;
int len, i;
svsk->sk_lastrecv = get_seconds();
clear_bit(SK_OLD, &svsk->sk_flags);
- rqstp->rq_secure = ntohs(rqstp->rq_addr.sin_port) < 1024;
+ rqstp->rq_secure = svc_port_is_privileged(svc_addr(rqstp));
rqstp->rq_chandle.defer = svc_defer;
if (serv->sv_stats)
* Create socket for RPC service.
*/
static int svc_create_socket(struct svc_serv *serv, int protocol,
- struct sockaddr_in *sin, int flags)
+ struct sockaddr *sin, int len, int flags)
{
struct svc_sock *svsk;
struct socket *sock;
int error;
int type;
+ char buf[RPC_MAX_ADDRBUFLEN];
- dprintk("svc: svc_create_socket(%s, %d, %u.%u.%u.%u:%d)\n",
- serv->sv_program->pg_name, protocol,
- NIPQUAD(sin->sin_addr.s_addr),
- ntohs(sin->sin_port));
+ dprintk("svc: svc_create_socket(%s, %d, %s)\n",
+ serv->sv_program->pg_name, protocol,
+ __svc_print_addr(sin, buf, sizeof(buf)));
if (protocol != IPPROTO_UDP && protocol != IPPROTO_TCP) {
printk(KERN_WARNING "svc: only UDP and TCP "
}
type = (protocol == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
- if ((error = sock_create_kern(PF_INET, type, protocol, &sock)) < 0)
+ error = sock_create_kern(sin->sa_family, type, protocol, &sock);
+ if (error < 0)
return error;
svc_reclassify_socket(sock);
if (type == SOCK_STREAM)
- sock->sk->sk_reuse = 1; /* allow address reuse */
- error = kernel_bind(sock, (struct sockaddr *) sin,
- sizeof(*sin));
+ sock->sk->sk_reuse = 1; /* allow address reuse */
+ error = kernel_bind(sock, sin, len);
if (error < 0)
goto bummer;
};
dprintk("svc: creating socket proto = %d\n", protocol);
- return svc_create_socket(serv, protocol, &sin, flags);
+ return svc_create_socket(serv, protocol, (struct sockaddr *) &sin,
+ sizeof(sin), flags);
}
/*
dr->handle.owner = rqstp->rq_server;
dr->prot = rqstp->rq_prot;
- dr->addr = rqstp->rq_addr;
+ memcpy(&dr->addr, &rqstp->rq_addr, rqstp->rq_addrlen);
+ dr->addrlen = rqstp->rq_addrlen;
dr->daddr = rqstp->rq_daddr;
dr->argslen = rqstp->rq_arg.len >> 2;
memcpy(dr->args, rqstp->rq_arg.head[0].iov_base-skip, dr->argslen<<2);
rqstp->rq_arg.page_len = 0;
rqstp->rq_arg.len = dr->argslen<<2;
rqstp->rq_prot = dr->prot;
- rqstp->rq_addr = dr->addr;
+ memcpy(&rqstp->rq_addr, &dr->addr, dr->addrlen);
+ rqstp->rq_addrlen = dr->addrlen;
rqstp->rq_daddr = dr->daddr;
rqstp->rq_respages = rqstp->rq_pages;
return dr->argslen<<2;