[tcpip] Allow for transmission to multicast IPv4 addresses
authorMichael Brown <mcb30@etherboot.org>
Wed, 21 Jan 2009 03:40:39 +0000 (03:40 +0000)
committerMichael Brown <mcb30@etherboot.org>
Wed, 21 Jan 2009 03:40:39 +0000 (03:40 +0000)
When sending to a multicast address, it may be necessary to specify
the source address explicitly, since the multicast destination address
does not provide enough information to deduce the source address via
the miniroute table.

Allow the source address specified via the data-xfer metadata to be
passed down through the TCP/IP stack to the IPv4 layer, which can use
it as a default source address.

src/include/gpxe/tcpip.h
src/net/icmpv6.c
src/net/ipv4.c
src/net/ipv6.c
src/net/tcp.c
src/net/tcpip.c
src/net/udp.c

index 9bc3cc4..da89530 100644 (file)
@@ -82,6 +82,7 @@ struct tcpip_net_protocol {
         *
         * @v iobuf             I/O buffer
         * @v tcpip_protocol    Transport-layer protocol
+        * @v st_src            Source address, or NULL to use default
         * @v st_dest           Destination address
         * @v netdev            Network device (or NULL to route automatically)
         * @v trans_csum        Transport-layer checksum to complete, or NULL
@@ -91,6 +92,7 @@ struct tcpip_net_protocol {
         */
        int ( * tx ) ( struct io_buffer *iobuf,
                       struct tcpip_protocol *tcpip_protocol,
+                      struct sockaddr_tcpip *st_src,
                       struct sockaddr_tcpip *st_dest,
                       struct net_device *netdev,
                       uint16_t *trans_csum );
@@ -107,7 +109,8 @@ struct tcpip_net_protocol {
 extern int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
                      struct sockaddr_tcpip *st_src,
                      struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum );
-extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip, 
+extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip,
+                     struct sockaddr_tcpip *st_src,
                      struct sockaddr_tcpip *st_dest,
                      struct net_device *netdev,
                      uint16_t *trans_csum );
index 7b7146c..237fc4a 100644 (file)
@@ -60,7 +60,7 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse
        st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
        
        /* Send packet over IP6 */
-       return tcpip_tx ( iobuf, &icmp6_protocol, &st_dest.st,
+       return tcpip_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
                          NULL, &nsolicit->csum );
 }
 
index 63dcca2..335048c 100644 (file)
@@ -286,6 +286,7 @@ static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
  *
  * @v iobuf            I/O buffer
  * @v tcpip            Transport-layer protocol
+ * @v st_src           Source network-layer address
  * @v st_dest          Destination network-layer address
  * @v netdev           Network device to use if no route found, or NULL
  * @v trans_csum       Transport-layer checksum to complete, or NULL
@@ -295,10 +296,12 @@ static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
  */
 static int ipv4_tx ( struct io_buffer *iobuf,
                     struct tcpip_protocol *tcpip_protocol,
+                    struct sockaddr_tcpip *st_src,
                     struct sockaddr_tcpip *st_dest,
                     struct net_device *netdev,
                     uint16_t *trans_csum ) {
        struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) );
+       struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src );
        struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
        struct ipv4_miniroute *miniroute;
        struct in_addr next_hop;
@@ -317,7 +320,11 @@ static int ipv4_tx ( struct io_buffer *iobuf,
 
        /* Use routing table to identify next hop and transmitting netdev */
        next_hop = iphdr->dest;
-       if ( ( miniroute = ipv4_route ( &next_hop ) ) ) {
+       if ( sin_src )
+               iphdr->src = sin_src->sin_addr;
+       if ( ( next_hop.s_addr != INADDR_BROADCAST ) &&
+            ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) &&
+            ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) {
                iphdr->src = miniroute->address;
                netdev = miniroute->netdev;
        }
index 3407d53..f7308bb 100644 (file)
@@ -176,6 +176,7 @@ void ipv6_dump ( struct ip6_header *ip6hdr ) {
  */
 static int ipv6_tx ( struct io_buffer *iobuf,
                     struct tcpip_protocol *tcpip,
+                    struct sockaddr_tcpip *st_src __unused,
                     struct sockaddr_tcpip *st_dest,
                     struct net_device *netdev,
                     uint16_t *trans_csum ) {
index 4e0db46..0f91bcf 100644 (file)
@@ -491,7 +491,7 @@ static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
        DBGC ( tcp, "\n" );
 
        /* Transmit packet */
-       return tcpip_tx ( iobuf, &tcp_protocol, &tcp->peer, NULL,
+       return tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL,
                          &tcphdr->csum );
 }
 
@@ -572,7 +572,7 @@ static int tcp_xmit_reset ( struct tcp_connection *tcp,
        DBGC ( tcp, "\n" );
 
        /* Transmit packet */
-       return tcpip_tx ( iobuf, &tcp_protocol, st_dest,
+       return tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest,
                          NULL, &tcphdr->csum );
 }
 
index 1bc8d1a..d4542b0 100644 (file)
@@ -64,14 +64,15 @@ int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto,
  *
  * @v iobuf            I/O buffer
  * @v tcpip_protocol   Transport-layer protocol
+ * @v st_src           Source address, or NULL to use route default
  * @v st_dest          Destination address
  * @v netdev           Network device to use if no route found, or NULL
  * @v trans_csum       Transport-layer checksum to complete, or NULL
  * @ret rc             Return status code
  */
 int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
-              struct sockaddr_tcpip *st_dest, struct net_device *netdev,
-              uint16_t *trans_csum ) {
+              struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest,
+              struct net_device *netdev, uint16_t *trans_csum ) {
        struct tcpip_net_protocol *tcpip_net;
 
        /* Hand off packet to the appropriate network-layer protocol */
@@ -79,8 +80,8 @@ int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol,
              tcpip_net < tcpip_net_protocols_end ; tcpip_net++ ) {
                if ( tcpip_net->sa_family == st_dest->st_family ) {
                        DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
-                       return tcpip_net->tx ( iobuf, tcpip_protocol, st_dest,
-                                              netdev, trans_csum );
+                       return tcpip_net->tx ( iobuf, tcpip_protocol, st_src,
+                                              st_dest, netdev, trans_csum );
                }
        }
        
index fddf81d..63c2d9e 100644 (file)
@@ -182,13 +182,13 @@ static void udp_close ( struct udp_connection *udp, int rc ) {
  *
  * @v udp              UDP connection
  * @v iobuf            I/O buffer
- * @v src_port         Source port, or 0 to use default
+ * @v src              Source address, or NULL to use default
  * @v dest             Destination address, or NULL to use default
  * @v netdev           Network device, or NULL to use default
  * @ret rc             Return status code
  */
 static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
-                   unsigned int src_port, struct sockaddr_tcpip *dest,
+                   struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
                    struct net_device *netdev ) {
                struct udp_header *udphdr;
        size_t len;
@@ -201,8 +201,8 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
        }
 
        /* Fill in default values if not explicitly provided */
-       if ( ! src_port )
-               src_port = udp->local.st_port;
+       if ( ! src )
+               src = &udp->local;
        if ( ! dest )
                dest = &udp->peer;
 
@@ -210,7 +210,7 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
        udphdr = iob_push ( iobuf, sizeof ( *udphdr ) );
        len = iob_len ( iobuf );
        udphdr->dest = dest->st_port;
-       udphdr->src = src_port;
+       udphdr->src = src->st_port;
        udphdr->len = htons ( len );
        udphdr->chksum = 0;
        udphdr->chksum = tcpip_chksum ( udphdr, len );
@@ -221,7 +221,7 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf,
               ntohs ( udphdr->len ) );
 
        /* Send it to the next layer for processing */
-       if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, dest, netdev,
+       if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev,
                               &udphdr->chksum ) ) != 0 ) {
                DBGC ( udp, "UDP %p could not transmit packet: %s\n",
                       udp, strerror ( rc ) );
@@ -399,22 +399,19 @@ static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
                                  struct xfer_metadata *meta ) {
        struct udp_connection *udp =
                container_of ( xfer, struct udp_connection, xfer );
-       struct sockaddr_tcpip *src;
+       struct sockaddr_tcpip *src = NULL;
        struct sockaddr_tcpip *dest = NULL;
        struct net_device *netdev = NULL;
-       unsigned int src_port = 0;
 
        /* Apply xfer metadata */
        if ( meta ) {
                src = ( struct sockaddr_tcpip * ) meta->src;
-               if ( src )
-                       src_port = src->st_port;
                dest = ( struct sockaddr_tcpip * ) meta->dest;
                netdev = meta->netdev;
        }
 
        /* Transmit data, if possible */
-       udp_tx ( udp, iobuf, src_port, dest, netdev );
+       udp_tx ( udp, iobuf, src, dest, netdev );
 
        return 0;
 }