diff --git a/net/slirp.c b/net/slirp.c index 99884de204..f6dc03963a 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -150,6 +150,7 @@ static int net_slirp_init(NetClientState *peer, const char *model, const char *vnameserver, const char *vnameserver6, const char *smb_export, const char *vsmbserver, const char **dnssearch, const char *vdomainname, + const char *tftp_server_name, Error **errp) { /* default settings according to historic slirp */ @@ -350,6 +351,20 @@ static int net_slirp_init(NetClientState *peer, const char *model, return -1; } + if (vdomainname && strlen(vdomainname) > 255) { + error_setg(errp, "'domainname' parameter cannot exceed 255 bytes"); + return -1; + } + + if (vhostname && strlen(vhostname) > 255) { + error_setg(errp, "'vhostname' parameter cannot exceed 255 bytes"); + return -1; + } + + if (tftp_server_name && strlen(tftp_server_name) > 255) { + error_setg(errp, "'tftp-server-name' parameter cannot exceed 255 bytes"); + return -1; + } nc = qemu_new_net_client(&net_slirp_info, peer, model, name); @@ -361,7 +376,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, s->slirp = slirp_init(restricted, ipv4, net, mask, host, ipv6, ip6_prefix, vprefix6_len, ip6_host, - vhostname, tftp_export, bootfile, dhcp, + vhostname, tftp_server_name, + tftp_export, bootfile, dhcp, dns, ip6_dns, dnssearch, vdomainname, s); QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); @@ -898,7 +914,8 @@ int net_init_slirp(const Netdev *netdev, const char *name, user->ipv6_host, user->hostname, user->tftp, user->bootfile, user->dhcpstart, user->dns, user->ipv6_dns, user->smb, - user->smbserver, dnssearch, user->domainname, errp); + user->smbserver, dnssearch, user->domainname, + user->tftp_server_name, errp); while (slirp_configs) { config = slirp_configs; diff --git a/qapi/net.json b/qapi/net.json index c86f351161..8f99fd911d 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -174,6 +174,8 @@ # # @guestfwd: forward guest TCP connections # +# @tftp-server-name: RFC2132 "TFTP server name" string (Since 3.1) +# # Since: 1.2 ## { 'struct': 'NetdevUserOptions', @@ -198,7 +200,8 @@ '*smb': 'str', '*smbserver': 'str', '*hostfwd': ['String'], - '*guestfwd': ['String'] } } + '*guestfwd': ['String'], + '*tftp-server-name': 'str' } } ## # @NetdevTapOptions: diff --git a/qemu-options.hx b/qemu-options.hx index 214ce396f9..08f8516a9a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1823,7 +1823,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " [,ipv6[=on|off]][,ipv6-net=addr[/int]][,ipv6-host=addr]\n" " [,restrict=on|off][,hostname=host][,dhcpstart=addr]\n" " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,domainname=domain]\n" - " [,tftp=dir][,bootfile=f][,hostfwd=rule][,guestfwd=rule]" + " [,tftp=dir][,tftp-server-name=name][,bootfile=f][,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 "[,smb=dir[,smbserver=addr]]\n" #endif @@ -2060,6 +2060,11 @@ server. The files in @var{dir} will be exposed as the root of a TFTP server. The TFTP client on the guest must be configured in binary mode (use the command @code{bin} of the Unix TFTP client). +@item tftp-server-name=@var{name} +In BOOTP reply, broadcast @var{name} as the "TFTP server name" (RFC2132 option +66). This can be used to advise the guest to load boot files or configurations +from a different server than the host address. + @item bootfile=@var{file} When using the user mode network stack, broadcast @var{file} as the BOOTP filename. In conjunction with @option{tftp}, this can be used to network boot diff --git a/slirp/bootp.c b/slirp/bootp.c index 9e7b53ba94..7b1af73c95 100644 --- a/slirp/bootp.c +++ b/slirp/bootp.c @@ -159,6 +159,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) struct in_addr preq_addr; int dhcp_msg_type, val; uint8_t *q; + uint8_t *end; uint8_t client_ethaddr[ETH_ALEN]; /* extract exact DHCP msg type */ @@ -240,6 +241,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */ q = rbp->bp_vend; + end = (uint8_t *)&rbp[1]; memcpy(q, rfc1533_cookie, 4); q += 4; @@ -292,24 +294,46 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) if (*slirp->client_hostname) { val = strlen(slirp->client_hostname); - *q++ = RFC1533_HOSTNAME; - *q++ = val; - memcpy(q, slirp->client_hostname, val); - q += val; + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting host name option."); + } else { + *q++ = RFC1533_HOSTNAME; + *q++ = val; + memcpy(q, slirp->client_hostname, val); + q += val; + } } if (slirp->vdomainname) { val = strlen(slirp->vdomainname); - *q++ = RFC1533_DOMAINNAME; - *q++ = val; - memcpy(q, slirp->vdomainname, val); - q += val; + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting domain name option."); + } else { + *q++ = RFC1533_DOMAINNAME; + *q++ = val; + memcpy(q, slirp->vdomainname, val); + q += val; + } + } + + if (slirp->tftp_server_name) { + val = strlen(slirp->tftp_server_name); + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting tftp-server-name option."); + } else { + *q++ = RFC2132_TFTP_SERVER_NAME; + *q++ = val; + memcpy(q, slirp->tftp_server_name, val); + q += val; + } } if (slirp->vdnssearch) { - size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend); val = slirp->vdnssearch_len; - if (val + 1 > spaceleft) { + if (q + val >= end) { g_warning("DHCP packet size exceeded, " "omitting domain-search option."); } else { @@ -331,6 +355,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) memcpy(q, nak_msg, sizeof(nak_msg) - 1); q += sizeof(nak_msg) - 1; } + assert(q < end); *q = RFC1533_END; daddr.sin_addr.s_addr = 0xffffffffu; diff --git a/slirp/bootp.h b/slirp/bootp.h index 394525733e..4043489835 100644 --- a/slirp/bootp.h +++ b/slirp/bootp.h @@ -70,6 +70,7 @@ #define RFC2132_MAX_SIZE 57 #define RFC2132_RENEWAL_TIME 58 #define RFC2132_REBIND_TIME 59 +#define RFC2132_TFTP_SERVER_NAME 66 #define DHCPDISCOVER 1 #define DHCPOFFER 2 diff --git a/slirp/libslirp.h b/slirp/libslirp.h index 740408a96e..42e42e9a2a 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -13,6 +13,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, bool in6_enabled, struct in6_addr vprefix_addr6, uint8_t vprefix_len, struct in6_addr vhost6, const char *vhostname, + const char *tftp_server_name, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, diff --git a/slirp/slirp.c b/slirp/slirp.c index 5c3bd6163f..51de41fc02 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -283,6 +283,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, bool in6_enabled, struct in6_addr vprefix_addr6, uint8_t vprefix_len, struct in6_addr vhost6, const char *vhostname, + const char *tftp_server_name, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, @@ -321,6 +322,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; slirp->vnameserver_addr6 = vnameserver6; + slirp->tftp_server_name = g_strdup(tftp_server_name); if (vdnssearch) { translate_dnssearch(slirp, vdnssearch); diff --git a/slirp/slirp.h b/slirp/slirp.h index 10b410898a..b80725a0d6 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -212,6 +212,7 @@ struct Slirp { /* tftp states */ char *tftp_prefix; struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; + char *tftp_server_name; ArpTable arp_table; NdpTable ndp_table;