#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>
}
}
+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;
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 = rqstp->rq_addrlen;
- 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;
}
}
+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.
*/
static int
svc_udp_recvfrom(struct svc_rqst *rqstp)
{
- struct sockaddr_in *sin = svc_addr_in(rqstp);
struct svc_sock *svsk = rqstp->rq_sock;
struct svc_serv *serv = svsk->sk_server;
struct sk_buff *skb;
len = skb->len - sizeof(struct udphdr);
rqstp->rq_arg.len = len;
- rqstp->rq_prot = IPPROTO_UDP;
-
- /* Get sender address */
- 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);
+ rqstp->rq_prot = IPPROTO_UDP;
- /* Remember which interface received this request */
- 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;
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",
* 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: %s\n",
serv->sv_name,
- __svc_print_addr((struct sockaddr *) &sin, buf,
- sizeof(buf)));
+ __svc_print_addr(sin, buf, sizeof(buf)));
}
dprintk("%s: connect from %s\n", serv->sv_name,
- __svc_print_addr((struct sockaddr *) &sin, buf,
- sizeof(buf)));
+ __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);
svc_recv(struct svc_rqst *rqstp, long timeout)
{
struct svc_sock *svsk = NULL;
- struct sockaddr_in *sin = svc_addr_in(rqstp);
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(sin->sin_port) < PROT_SOCK;
+ 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;
dprintk("svc: svc_create_socket(%s, %d, %s)\n",
serv->sv_program->pg_name, protocol,
- __svc_print_addr((struct sockaddr *) sin, buf,
- sizeof(buf)));
+ __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);
}
/*