pppol2tp.c
来自「linux 内核源代码」· C语言 代码 · 共 2,472 行 · 第 1/5 页
C
2,472 行
tunnel = pppol2tp_sock_to_tunnel(sock); if (tunnel == NULL) goto no_tunnel; /* UDP always verifies the packet length. */ __skb_pull(skb, sizeof(struct udphdr)); /* Short packet? */ if (!pskb_may_pull(skb, 12)) { PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO, "%s: recv short packet (len=%d)\n", tunnel->name, skb->len); goto error; } /* Point to L2TP header */ optr = ptr = skb->data; /* Get L2TP header flags */ hdrflags = ntohs(*(__be16*)ptr); /* Trace packet contents, if enabled */ if (tunnel->debug & PPPOL2TP_MSG_DATA) { length = min(16u, skb->len); if (!pskb_may_pull(skb, length)) goto error; printk(KERN_DEBUG "%s: recv: ", tunnel->name); offset = 0; do { printk(" %02X", ptr[offset]); } while (++offset < length); printk("\n"); } /* Get length of L2TP packet */ length = skb->len; /* If type is control packet, it is handled by userspace. */ if (hdrflags & L2TP_HDRFLAG_T) { PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, "%s: recv control packet, len=%d\n", tunnel->name, length); goto error; } /* Skip flags */ ptr += 2; /* If length is present, skip it */ if (hdrflags & L2TP_HDRFLAG_L) ptr += 2; /* Extract tunnel and session ID */ tunnel_id = ntohs(*(__be16 *) ptr); ptr += 2; session_id = ntohs(*(__be16 *) ptr); ptr += 2; /* Find the session context */ session = pppol2tp_session_find(tunnel, session_id); if (!session) { /* Not found? Pass to userspace to deal with */ PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO, "%s: no socket found (%hu/%hu). Passing up.\n", tunnel->name, tunnel_id, session_id); goto error; } sock_hold(session->sock); /* The ref count on the socket was increased by the above call since * we now hold a pointer to the session. Take care to do sock_put() * when exiting this function from now on... */ /* Handle the optional sequence numbers. If we are the LAC, * enable/disable sequence numbers under the control of the LNS. If * no sequence numbers present but we were expecting them, discard * frame. */ if (hdrflags & L2TP_HDRFLAG_S) { u16 ns, nr; ns = ntohs(*(__be16 *) ptr); ptr += 2; nr = ntohs(*(__be16 *) ptr); ptr += 2; /* Received a packet with sequence numbers. If we're the LNS, * check if we sre sending sequence numbers and if not, * configure it so. */ if ((!session->lns_mode) && (!session->send_seq)) { PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_INFO, "%s: requested to enable seq numbers by LNS\n", session->name); session->send_seq = -1; } /* Store L2TP info in the skb */ PPPOL2TP_SKB_CB(skb)->ns = ns; PPPOL2TP_SKB_CB(skb)->nr = nr; PPPOL2TP_SKB_CB(skb)->has_seq = 1; PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, "%s: recv data ns=%hu, nr=%hu, session nr=%hu\n", session->name, ns, nr, session->nr); } else { /* No sequence numbers. * If user has configured mandatory sequence numbers, discard. */ if (session->recv_seq) { PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_WARNING, "%s: recv data has no seq numbers when required. " "Discarding\n", session->name); session->stats.rx_seq_discards++; goto discard; } /* If we're the LAC and we're sending sequence numbers, the * LNS has requested that we no longer send sequence numbers. * If we're the LNS and we're sending sequence numbers, the * LAC is broken. Discard the frame. */ if ((!session->lns_mode) && (session->send_seq)) { PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_INFO, "%s: requested to disable seq numbers by LNS\n", session->name); session->send_seq = 0; } else if (session->send_seq) { PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_WARNING, "%s: recv data has no seq numbers when required. " "Discarding\n", session->name); session->stats.rx_seq_discards++; goto discard; } /* Store L2TP info in the skb */ PPPOL2TP_SKB_CB(skb)->has_seq = 0; } /* If offset bit set, skip it. */ if (hdrflags & L2TP_HDRFLAG_O) { offset = ntohs(*(__be16 *)ptr); ptr += 2 + offset; } offset = ptr - optr; if (!pskb_may_pull(skb, offset)) goto discard; __skb_pull(skb, offset); /* Skip PPP header, if present. In testing, Microsoft L2TP clients * don't send the PPP header (PPP header compression enabled), but * other clients can include the header. So we cope with both cases * here. The PPP header is always FF03 when using L2TP. * * Note that skb->data[] isn't dereferenced from a u16 ptr here since * the field may be unaligned. */ if (!pskb_may_pull(skb, 2)) goto discard; if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03)) skb_pull(skb, 2); /* Prepare skb for adding to the session's reorder_q. Hold * packets for max reorder_timeout or 1 second if not * reordering. */ PPPOL2TP_SKB_CB(skb)->length = length; PPPOL2TP_SKB_CB(skb)->expires = jiffies + (session->reorder_timeout ? session->reorder_timeout : HZ); /* Add packet to the session's receive queue. Reordering is done here, if * enabled. Saved L2TP protocol info is stored in skb->sb[]. */ if (PPPOL2TP_SKB_CB(skb)->has_seq) { if (session->reorder_timeout != 0) { /* Packet reordering enabled. Add skb to session's * reorder queue, in order of ns. */ pppol2tp_recv_queue_skb(session, skb); } else { /* Packet reordering disabled. Discard out-of-sequence * packets */ if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) { session->stats.rx_seq_discards++; PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, "%s: oos pkt %hu len %d discarded, " "waiting for %hu, reorder_q_len=%d\n", session->name, PPPOL2TP_SKB_CB(skb)->ns, PPPOL2TP_SKB_CB(skb)->length, session->nr, skb_queue_len(&session->reorder_q)); goto discard; } skb_queue_tail(&session->reorder_q, skb); } } else { /* No sequence numbers. Add the skb to the tail of the * reorder queue. This ensures that it will be * delivered after all previous sequenced skbs. */ skb_queue_tail(&session->reorder_q, skb); } /* Try to dequeue as many skbs from reorder_q as we can. */ pppol2tp_recv_dequeue(session); return 0;discard: session->stats.rx_errors++; kfree_skb(skb); sock_put(session->sock); return 0;error: /* Put UDP header back */ __skb_push(skb, sizeof(struct udphdr));no_tunnel: return 1;}/* UDP encapsulation receive handler. See net/ipv4/udp.c. * Return codes: * 0 : success. * <0: error * >0: skb should be passed up to userspace as UDP. */static int pppol2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb){ struct pppol2tp_tunnel *tunnel; tunnel = pppol2tp_sock_to_tunnel(sk); if (tunnel == NULL) goto pass_up; PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, "%s: received %d bytes\n", tunnel->name, skb->len); if (pppol2tp_recv_core(sk, skb)) goto pass_up; return 0;pass_up: return 1;}/* Receive message. This is the recvmsg for the PPPoL2TP socket. */static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags){ int err; struct sk_buff *skb; struct sock *sk = sock->sk; err = -EIO; if (sk->sk_state & PPPOX_BOUND) goto end; msg->msg_namelen = 0; err = 0; skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); if (skb) { err = memcpy_toiovec(msg->msg_iov, (unsigned char *) skb->data, skb->len); if (err < 0) goto do_skb_free; err = skb->len; }do_skb_free: kfree_skb(skb);end: return err;}/************************************************************************ * Transmit handling ***********************************************************************//* Tell how big L2TP headers are for a particular session. This * depends on whether sequence numbers are being used. */static inline int pppol2tp_l2tp_header_len(struct pppol2tp_session *session){ if (session->send_seq) return PPPOL2TP_L2TP_HDR_SIZE_SEQ; return PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;}/* Build an L2TP header for the session into the buffer provided. */static void pppol2tp_build_l2tp_header(struct pppol2tp_session *session, void *buf){ __be16 *bufp = buf; u16 flags = L2TP_HDR_VER; if (session->send_seq) flags |= L2TP_HDRFLAG_S; /* Setup L2TP header. * FIXME: Can this ever be unaligned? Is direct dereferencing of * 16-bit header fields safe here for all architectures? */ *bufp++ = htons(flags); *bufp++ = htons(session->tunnel_addr.d_tunnel); *bufp++ = htons(session->tunnel_addr.d_session); if (session->send_seq) { *bufp++ = htons(session->ns); *bufp++ = 0; session->ns++; PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, "%s: updated ns to %hu\n", session->name, session->ns); }}/* This is the sendmsg for the PPPoL2TP pppol2tp_session socket. We come here * when a user application does a sendmsg() on the session socket. L2TP and * PPP headers must be inserted into the user's data. */static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len){ static const unsigned char ppph[2] = { 0xff, 0x03 }; struct sock *sk = sock->sk; struct inet_sock *inet; __wsum csum = 0; struct sk_buff *skb; int error; int hdr_len; struct pppol2tp_session *session; struct pppol2tp_tunnel *tunnel; struct udphdr *uh; unsigned int len; error = -ENOTCONN; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) goto error; /* Get session and tunnel contexts */ error = -EBADF; session = pppol2tp_sock_to_session(sk); if (session == NULL) goto error; tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); if (tunnel == NULL) goto error; /* What header length is configured for this session? */ hdr_len = pppol2tp_l2tp_header_len(session); /* Allocate a socket buffer */ error = -ENOMEM; skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) + sizeof(struct udphdr) + hdr_len + sizeof(ppph) + total_len, 0, GFP_KERNEL); if (!skb) goto error; /* Reserve space for headers. */ skb_reserve(skb, NET_SKB_PAD); skb_reset_network_header(skb); skb_reserve(skb, sizeof(struct iphdr)); skb_reset_transport_header(skb); /* Build UDP header */ inet = inet_sk(session->tunnel_sock); uh = (struct udphdr *) skb->data; uh->source = inet->sport; uh->dest = inet->dport; uh->len = htons(hdr_len + sizeof(ppph) + total_len); uh->check = 0; skb_put(skb, sizeof(struct udphdr)); /* Build L2TP header */ pppol2tp_build_l2tp_header(session, skb->data); skb_put(skb, hdr_len); /* Add PPP header */ skb->data[0] = ppph[0]; skb->data[1] = ppph[1]; skb_put(skb, 2); /* Copy user data into skb */ error = memcpy_fromiovec(skb->data, m->msg_iov, total_len); if (error < 0) { kfree_skb(skb); goto error; } skb_put(skb, total_len); /* Calculate UDP checksum if configured to do so */ if (session->tunnel_sock->sk_no_check != UDP_CSUM_NOXMIT) csum = udp_csum_outgoing(sk, skb); /* Debug */ if (session->send_seq) PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, "%s: send %Zd bytes, ns=%hu\n", session->name, total_len, session->ns - 1); else PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG, "%s: send %Zd bytes\n", session->name, total_len); if (session->debug & PPPOL2TP_MSG_DATA) { int i; unsigned char *datap = skb->data; printk(KERN_DEBUG "%s: xmit:", session->name); for (i = 0; i < total_len; i++) { printk(" %02X", *datap++); if (i == 15) { printk(" ..."); break; } } printk("\n"); } /* Queue the packet to IP for output */ len = skb->len; error = ip_queue_xmit(skb, 1); /* Update stats */ if (error >= 0) { tunnel->stats.tx_packets++; tunnel->stats.tx_bytes += len; session->stats.tx_packets++; session->stats.tx_bytes += len; } else { tunnel->stats.tx_errors++; session->stats.tx_errors++; }error: return error;}/* Transmit function called by generic PPP driver. Sends PPP frame * over PPPoL2TP socket. * * This is almost the same as pppol2tp_sendmsg(), but rather than * being called with a msghdr from userspace, it is called with a skb * from the kernel. * * The supplied skb from ppp doesn't have enough headroom for the * insertion of L2TP, UDP and IP headers so we need to allocate more * headroom in the skb. This will create a cloned skb. But we must be * careful in the error case because the caller will expect to free * the skb it supplied, not our cloned skb. So we take care to always * leave the original skb unfreed if we return an error. */static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb){ static const u8 ppph[2] = { 0xff, 0x03 }; struct sock *sk = (struct sock *) chan->private; struct sock *sk_tun; int hdr_len; struct pppol2tp_session *session; struct pppol2tp_tunnel *tunnel; int rc; int headroom; int data_len = skb->len; struct inet_sock *inet; __wsum csum = 0; struct udphdr *uh; unsigned int len; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) goto abort; /* Get session and tunnel contexts from the socket */ session = pppol2tp_sock_to_session(sk); if (session == NULL) goto abort; sk_tun = session->tunnel_sock; if (sk_tun == NULL) goto abort; tunnel = pppol2tp_sock_to_tunnel(sk_tun); if (tunnel == NULL)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?