The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/net/debugnet_inet.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2019 Isilon Systems, LLC.
    5  * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved.
    6  * Copyright (c) 2000 Darrell Anderson
    7  * All rights reserved.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include "opt_inet.h"
   35 
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/errno.h>
   39 #include <sys/socket.h>
   40 #include <sys/sysctl.h>
   41 
   42 #include <net/ethernet.h>
   43 #include <net/if.h>
   44 #include <net/if_arp.h>
   45 #include <net/if_dl.h>
   46 #include <net/if_types.h>
   47 #include <net/if_var.h>
   48 #include <net/if_private.h>
   49 
   50 #include <netinet/in.h>
   51 #include <netinet/in_systm.h>
   52 #include <netinet/in_var.h>
   53 #include <netinet/ip.h>
   54 #include <netinet/ip_var.h>
   55 #include <netinet/ip_options.h>
   56 #include <netinet/udp.h>
   57 #include <netinet/udp_var.h>
   58 
   59 #include <machine/in_cksum.h>
   60 #include <machine/pcb.h>
   61 
   62 #include <net/debugnet.h>
   63 #define DEBUGNET_INTERNAL
   64 #include <net/debugnet_int.h>
   65 
   66 int debugnet_arp_nretries = 3;
   67 SYSCTL_INT(_net_debugnet, OID_AUTO, arp_nretries, CTLFLAG_RWTUN,
   68     &debugnet_arp_nretries, 0,
   69     "Number of ARP attempts before giving up");
   70 
   71 /*
   72  * Handler for IP packets: checks their sanity and then processes any debugnet
   73  * ACK packets it finds.
   74  *
   75  * It needs to partially replicate the behaviour of ip_input() and udp_input().
   76  *
   77  * Parameters:
   78  *      pcb     a pointer to the live debugnet PCB
   79  *      mb      a pointer to an mbuf * containing the packet received
   80  *              Updates *mb if m_pullup et al change the pointer
   81  *              Assumes the calling function will take care of freeing the mbuf
   82  */
   83 void
   84 debugnet_handle_ip(struct debugnet_pcb *pcb, struct mbuf **mb)
   85 {
   86         struct ip *ip;
   87         struct mbuf *m;
   88         unsigned short hlen;
   89 
   90         if (pcb->dp_state < DN_STATE_HAVE_GW_MAC)
   91                 return;
   92 
   93         /* IP processing. */
   94         m = *mb;
   95         if (m->m_pkthdr.len < sizeof(struct ip)) {
   96                 DNETDEBUG("dropping packet too small for IP header\n");
   97                 return;
   98         }
   99         if (m->m_len < sizeof(struct ip)) {
  100                 m = m_pullup(m, sizeof(struct ip));
  101                 *mb = m;
  102                 if (m == NULL) {
  103                         DNETDEBUG("m_pullup failed\n");
  104                         return;
  105                 }
  106         }
  107         ip = mtod(m, struct ip *);
  108 
  109         /* IP version. */
  110         if (ip->ip_v != IPVERSION) {
  111                 DNETDEBUG("bad IP version %d\n", ip->ip_v);
  112                 return;
  113         }
  114 
  115         /* Header length. */
  116         hlen = ip->ip_hl << 2;
  117         if (hlen < sizeof(struct ip)) {
  118                 DNETDEBUG("bad IP header length (%hu)\n", hlen);
  119                 return;
  120         }
  121         if (hlen > m->m_len) {
  122                 m = m_pullup(m, hlen);
  123                 *mb = m;
  124                 if (m == NULL) {
  125                         DNETDEBUG("m_pullup failed\n");
  126                         return;
  127                 }
  128                 ip = mtod(m, struct ip *);
  129         }
  130         /* Ignore packets with IP options. */
  131         if (hlen > sizeof(struct ip)) {
  132                 DNETDEBUG("drop packet with IP options\n");
  133                 return;
  134         }
  135 
  136 #ifdef INVARIANTS
  137         if ((IN_LOOPBACK(ntohl(ip->ip_dst.s_addr)) ||
  138             IN_LOOPBACK(ntohl(ip->ip_src.s_addr))) &&
  139             (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
  140                 DNETDEBUG("Bad IP header (RFC1122)\n");
  141                 return;
  142         }
  143 #endif
  144 
  145         /* Checksum. */
  146         if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) {
  147                 if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 0) {
  148                         DNETDEBUG("bad IP checksum\n");
  149                         return;
  150                 }
  151         } else {
  152                 /* XXX */ ;
  153         }
  154 
  155         /* Convert fields to host byte order. */
  156         ip->ip_len = ntohs(ip->ip_len);
  157         if (ip->ip_len < hlen) {
  158                 DNETDEBUG("IP packet smaller (%hu) than header (%hu)\n",
  159                     ip->ip_len, hlen);
  160                 return;
  161         }
  162         if (m->m_pkthdr.len < ip->ip_len) {
  163                 DNETDEBUG("IP packet bigger (%hu) than ethernet packet (%d)\n",
  164                     ip->ip_len, m->m_pkthdr.len);
  165                 return;
  166         }
  167         if (m->m_pkthdr.len > ip->ip_len) {
  168                 /* Truncate the packet to the IP length. */
  169                 if (m->m_len == m->m_pkthdr.len) {
  170                         m->m_len = ip->ip_len;
  171                         m->m_pkthdr.len = ip->ip_len;
  172                 } else
  173                         m_adj(m, ip->ip_len - m->m_pkthdr.len);
  174         }
  175 
  176         ip->ip_off = ntohs(ip->ip_off);
  177 
  178         /* Check that the source is the server's IP. */
  179         if (ip->ip_src.s_addr != pcb->dp_server) {
  180                 DNETDEBUG("drop packet not from server (from 0x%x)\n",
  181                     ip->ip_src.s_addr);
  182                 return;
  183         }
  184 
  185         /* Check if the destination IP is ours. */
  186         if (ip->ip_dst.s_addr != pcb->dp_client) {
  187                 DNETDEBUGV("drop packet not to our IP\n");
  188                 return;
  189         }
  190 
  191         if (ip->ip_p != IPPROTO_UDP) {
  192                 DNETDEBUG("drop non-UDP packet\n");
  193                 return;
  194         }
  195 
  196         /* Do not deal with fragments. */
  197         if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) {
  198                 DNETDEBUG("drop fragmented packet\n");
  199                 return;
  200         }
  201 
  202         if ((m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) != 0) {
  203                 if ((m->m_pkthdr.csum_flags & CSUM_DATA_VALID) == 0) {
  204                         DNETDEBUG("bad UDP checksum\n");
  205                         return;
  206                 }
  207         } else {
  208                 /* XXX */ ;
  209         }
  210 
  211         /* UDP custom is to have packet length not include IP header. */
  212         ip->ip_len -= hlen;
  213 
  214         /* Checked above before decoding IP header. */
  215         MPASS(m->m_pkthdr.len >= sizeof(struct ipovly));
  216 
  217         /* Put the UDP header at start of chain. */
  218         m_adj(m, sizeof(struct ipovly));
  219         debugnet_handle_udp(pcb, mb);
  220 }
  221 
  222 /*
  223  * Builds and sends a single ARP request to locate the L2 address for a given
  224  * INET address.
  225  *
  226  * Return value:
  227  *      0 on success
  228  *      errno on error
  229  */
  230 static int
  231 debugnet_send_arp(struct debugnet_pcb *pcb, in_addr_t dst)
  232 {
  233         struct ether_addr bcast;
  234         struct arphdr *ah;
  235         struct ifnet *ifp;
  236         struct mbuf *m;
  237         int pktlen;
  238 
  239         ifp = pcb->dp_ifp;
  240 
  241         /* Fill-up a broadcast address. */
  242         memset(&bcast, 0xFF, ETHER_ADDR_LEN);
  243         m = m_gethdr(M_NOWAIT, MT_DATA);
  244         if (m == NULL) {
  245                 printf("%s: Out of mbufs\n", __func__);
  246                 return (ENOBUFS);
  247         }
  248         pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
  249         m->m_len = pktlen;
  250         m->m_pkthdr.len = pktlen;
  251         MH_ALIGN(m, pktlen);
  252         ah = mtod(m, struct arphdr *);
  253         ah->ar_hrd = htons(ARPHRD_ETHER);
  254         ah->ar_pro = htons(ETHERTYPE_IP);
  255         ah->ar_hln = ETHER_ADDR_LEN;
  256         ah->ar_pln = sizeof(struct in_addr);
  257         ah->ar_op = htons(ARPOP_REQUEST);
  258         memcpy(ar_sha(ah), IF_LLADDR(ifp), ETHER_ADDR_LEN);
  259         ((struct in_addr *)ar_spa(ah))->s_addr = pcb->dp_client;
  260         bzero(ar_tha(ah), ETHER_ADDR_LEN);
  261         ((struct in_addr *)ar_tpa(ah))->s_addr = dst;
  262         return (debugnet_ether_output(m, ifp, bcast, ETHERTYPE_ARP));
  263 }
  264 
  265 /*
  266  * Handler for ARP packets: checks their sanity and then
  267  * 1. If the ARP is a request for our IP, respond with our MAC address
  268  * 2. If the ARP is a response from our server, record its MAC address
  269  *
  270  * It needs to replicate partially the behaviour of arpintr() and
  271  * in_arpinput().
  272  *
  273  * Parameters:
  274  *      pcb     a pointer to the live debugnet PCB
  275  *      mb      a pointer to an mbuf * containing the packet received
  276  *              Updates *mb if m_pullup et al change the pointer
  277  *              Assumes the calling function will take care of freeing the mbuf
  278  */
  279 void
  280 debugnet_handle_arp(struct debugnet_pcb *pcb, struct mbuf **mb)
  281 {
  282         char buf[INET_ADDRSTRLEN];
  283         struct in_addr isaddr, itaddr;
  284         struct ether_addr dst;
  285         struct mbuf *m;
  286         struct arphdr *ah;
  287         struct ifnet *ifp;
  288         uint8_t *enaddr;
  289         int req_len, op;
  290 
  291         m = *mb;
  292         ifp = m->m_pkthdr.rcvif;
  293         if (m->m_len < sizeof(struct arphdr)) {
  294                 m = m_pullup(m, sizeof(struct arphdr));
  295                 *mb = m;
  296                 if (m == NULL) {
  297                         DNETDEBUG("runt packet: m_pullup failed\n");
  298                         return;
  299                 }
  300         }
  301 
  302         ah = mtod(m, struct arphdr *);
  303         if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) {
  304                 DNETDEBUG("unknown hardware address 0x%2D)\n",
  305                     (unsigned char *)&ah->ar_hrd, "");
  306                 return;
  307         }
  308         if (ntohs(ah->ar_pro) != ETHERTYPE_IP) {
  309                 DNETDEBUG("drop ARP for unknown protocol %d\n",
  310                     ntohs(ah->ar_pro));
  311                 return;
  312         }
  313         req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr));
  314         if (m->m_len < req_len) {
  315                 m = m_pullup(m, req_len);
  316                 *mb = m;
  317                 if (m == NULL) {
  318                         DNETDEBUG("runt packet: m_pullup failed\n");
  319                         return;
  320                 }
  321         }
  322         ah = mtod(m, struct arphdr *);
  323 
  324         op = ntohs(ah->ar_op);
  325         memcpy(&isaddr, ar_spa(ah), sizeof(isaddr));
  326         memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr));
  327         enaddr = (uint8_t *)IF_LLADDR(ifp);
  328 
  329         if (memcmp(ar_sha(ah), enaddr, ifp->if_addrlen) == 0) {
  330                 DNETDEBUG("ignoring ARP from myself\n");
  331                 return;
  332         }
  333 
  334         if (isaddr.s_addr == pcb->dp_client) {
  335                 printf("%s: %*D is using my IP address %s!\n", __func__,
  336                     ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
  337                     inet_ntoa_r(isaddr, buf));
  338                 return;
  339         }
  340 
  341         if (memcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen) == 0) {
  342                 DNETDEBUG("ignoring ARP from broadcast address\n");
  343                 return;
  344         }
  345 
  346         if (op == ARPOP_REPLY) {
  347                 if (isaddr.s_addr != pcb->dp_gateway &&
  348                     isaddr.s_addr != pcb->dp_server) {
  349                         inet_ntoa_r(isaddr, buf);
  350                         DNETDEBUG("ignoring ARP reply from %s (not configured"
  351                             " server or gateway)\n", buf);
  352                         return;
  353                 }
  354                 if (pcb->dp_state >= DN_STATE_HAVE_GW_MAC) {
  355                         inet_ntoa_r(isaddr, buf);
  356                         DNETDEBUG("ignoring server ARP reply from %s (already"
  357                             " have gateway address)\n", buf);
  358                         return;
  359                 }
  360                 MPASS(pcb->dp_state == DN_STATE_INIT);
  361                 memcpy(pcb->dp_gw_mac.octet, ar_sha(ah),
  362                     min(ah->ar_hln, ETHER_ADDR_LEN));
  363                 
  364                 DNETDEBUG("got server MAC address %6D\n",
  365                     pcb->dp_gw_mac.octet, ":");
  366 
  367                 pcb->dp_state = DN_STATE_HAVE_GW_MAC;
  368                 return;
  369         }
  370 
  371         if (op != ARPOP_REQUEST) {
  372                 DNETDEBUG("ignoring ARP non-request/reply\n");
  373                 return;
  374         }
  375 
  376         if (itaddr.s_addr != pcb->dp_client) {
  377                 DNETDEBUG("ignoring ARP not to our IP\n");
  378                 return;
  379         }
  380 
  381         memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
  382         memcpy(ar_sha(ah), enaddr, ah->ar_hln);
  383         memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln);
  384         memcpy(ar_spa(ah), &itaddr, ah->ar_pln);
  385         ah->ar_op = htons(ARPOP_REPLY);
  386         ah->ar_pro = htons(ETHERTYPE_IP);
  387         m->m_flags &= ~(M_BCAST|M_MCAST);
  388         m->m_len = arphdr_len(ah);
  389         m->m_pkthdr.len = m->m_len;
  390 
  391         memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
  392         debugnet_ether_output(m, ifp, dst, ETHERTYPE_ARP);
  393         *mb = NULL;
  394 }
  395 
  396 /*
  397  * Sends ARP requests to locate the server and waits for a response.
  398  * We first try to ARP the server itself, and fall back to the provided
  399  * gateway if the server appears to be off-link.
  400  *
  401  * Return value:
  402  *      0 on success
  403  *      errno on error
  404  */
  405 int
  406 debugnet_arp_gw(struct debugnet_pcb *pcb)
  407 {
  408         in_addr_t dst;
  409         int error, polls, retries;
  410 
  411         dst = pcb->dp_server;
  412 restart:
  413         for (retries = 0; retries < debugnet_arp_nretries; retries++) {
  414                 error = debugnet_send_arp(pcb, dst);
  415                 if (error != 0)
  416                         return (error);
  417                 for (polls = 0; polls < debugnet_npolls &&
  418                     pcb->dp_state < DN_STATE_HAVE_GW_MAC; polls++) {
  419                         debugnet_network_poll(pcb);
  420                         DELAY(500);
  421                 }
  422                 if (pcb->dp_state >= DN_STATE_HAVE_GW_MAC)
  423                         break;
  424                 printf("(ARP retry)");
  425         }
  426         if (pcb->dp_state >= DN_STATE_HAVE_GW_MAC)
  427                 return (0);
  428         if (dst == pcb->dp_server) {
  429                 printf("\nFailed to ARP server");
  430                 if (pcb->dp_gateway != INADDR_ANY) {
  431                         printf(", trying to reach gateway...\n");
  432                         dst = pcb->dp_gateway;
  433                         goto restart;
  434                 } else
  435                         printf(".\n");
  436         } else
  437                 printf("\nFailed to ARP gateway.\n");
  438 
  439         return (ETIMEDOUT);
  440 }
  441 
  442 /*
  443  * Unreliable IPv4 transmission of an mbuf chain to the debugnet server
  444  * Note: can't handle fragmentation; fails if the packet is larger than
  445  *       ifp->if_mtu after adding the UDP/IP headers
  446  *
  447  * Parameters:
  448  *      pcb     The debugnet context block
  449  *      m       mbuf chain
  450  *
  451  * Returns:
  452  *      int     see errno.h, 0 for success
  453  */
  454 int
  455 debugnet_ip_output(struct debugnet_pcb *pcb, struct mbuf *m)
  456 {
  457         struct udphdr *udp;
  458         struct ifnet *ifp;
  459         struct ip *ip;
  460 
  461         MPASS(pcb->dp_state >= DN_STATE_HAVE_GW_MAC);
  462 
  463         ifp = pcb->dp_ifp;
  464 
  465         M_PREPEND(m, sizeof(*ip), M_NOWAIT);
  466         if (m == NULL) {
  467                 printf("%s: out of mbufs\n", __func__);
  468                 return (ENOBUFS);
  469         }
  470 
  471         if (m->m_pkthdr.len > ifp->if_mtu) {
  472                 printf("%s: Packet is too big: %d > MTU %u\n", __func__,
  473                     m->m_pkthdr.len, ifp->if_mtu);
  474                 m_freem(m);
  475                 return (ENOBUFS);
  476         }
  477 
  478         ip = mtod(m, void *);
  479         udp = (void *)(ip + 1);
  480 
  481         memset(ip, 0, offsetof(struct ip, ip_p));
  482         ip->ip_p = IPPROTO_UDP;
  483         ip->ip_sum = udp->uh_ulen;
  484         ip->ip_src = (struct in_addr) { pcb->dp_client };
  485         ip->ip_dst = (struct in_addr) { pcb->dp_server };
  486 
  487         /* Compute UDP-IPv4 checksum. */
  488         udp->uh_sum = in_cksum(m, m->m_pkthdr.len);
  489         if (udp->uh_sum == 0)
  490                 udp->uh_sum = 0xffff;
  491 
  492         ip->ip_v = IPVERSION;
  493         ip->ip_hl = sizeof(*ip) >> 2;
  494         ip->ip_tos = 0;
  495         ip->ip_len = htons(m->m_pkthdr.len);
  496         ip->ip_id = 0;
  497         ip->ip_off = htons(IP_DF);
  498         ip->ip_ttl = 255;
  499         ip->ip_sum = 0;
  500         ip->ip_sum = in_cksum(m, sizeof(struct ip));
  501 
  502         return (debugnet_ether_output(m, ifp, pcb->dp_gw_mac, ETHERTYPE_IP));
  503 }

Cache object: 8392644841adf96c8a0024bc3380fdc3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.