From d230b53df2f44da477742094e5bbcc1b1520347c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 21 Jan 2009 03:40:39 +0000 Subject: [PATCH] [tcpip] Allow for transmission to multicast IPv4 addresses 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 | 5 ++++- src/net/icmpv6.c | 2 +- src/net/ipv4.c | 9 ++++++++- src/net/ipv6.c | 1 + src/net/tcp.c | 4 ++-- src/net/tcpip.c | 9 +++++---- src/net/udp.c | 19 ++++++++----------- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/include/gpxe/tcpip.h b/src/include/gpxe/tcpip.h index 9bc3cc4a..da89530e 100644 --- a/src/include/gpxe/tcpip.h +++ b/src/include/gpxe/tcpip.h @@ -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 ); diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c index 7b7146c2..237fc4a6 100644 --- a/src/net/icmpv6.c +++ b/src/net/icmpv6.c @@ -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 ); } diff --git a/src/net/ipv4.c b/src/net/ipv4.c index 63dcca28..335048c8 100644 --- a/src/net/ipv4.c +++ b/src/net/ipv4.c @@ -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; } diff --git a/src/net/ipv6.c b/src/net/ipv6.c index 3407d538..f7308bb4 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -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 ) { diff --git a/src/net/tcp.c b/src/net/tcp.c index 4e0db464..0f91bcf9 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -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 ); } diff --git a/src/net/tcpip.c b/src/net/tcpip.c index 1bc8d1a3..d4542b05 100644 --- a/src/net/tcpip.c +++ b/src/net/tcpip.c @@ -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 ); } } diff --git a/src/net/udp.c b/src/net/udp.c index fddf81df..63c2d9ee 100644 --- a/src/net/udp.c +++ b/src/net/udp.c @@ -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; }