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; }