Skip to content

Commit 1ce09e8

Browse files
haiyangzdavem330
authored andcommitted
hyperv: Add support for setting MAC from within guests
This adds support for setting synthetic NIC MAC address from within Linux guests. Before using this feature, the option "spoofing of MAC address" should be enabled at the Hyper-V manager / Settings of the synthetic NIC. Thanks to Kin Cho <[email protected]> for the initial implementation and tests. And, thanks to Long Li <[email protected]> for the debugging works. Reported-and-tested-by: Kin Cho <[email protected]> Reported-by: Long Li <[email protected]> Signed-off-by: Haiyang Zhang <[email protected]> Reviewed-by: K. Y. Srinivasan <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5e96855 commit 1ce09e8

File tree

3 files changed

+109
-1
lines changed

3 files changed

+109
-1
lines changed

drivers/net/hyperv/hyperv_net.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ int rndis_filter_send(struct hv_device *dev,
131131
struct hv_netvsc_packet *pkt);
132132

133133
int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter);
134+
int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac);
134135

135136

136137
#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF)

drivers/net/hyperv/netvsc_drv.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,34 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu)
341341
return 0;
342342
}
343343

344+
345+
static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
346+
{
347+
struct net_device_context *ndevctx = netdev_priv(ndev);
348+
struct hv_device *hdev = ndevctx->device_ctx;
349+
struct sockaddr *addr = p;
350+
char save_adr[14];
351+
unsigned char save_aatype;
352+
int err;
353+
354+
memcpy(save_adr, ndev->dev_addr, ETH_ALEN);
355+
save_aatype = ndev->addr_assign_type;
356+
357+
err = eth_mac_addr(ndev, p);
358+
if (err != 0)
359+
return err;
360+
361+
err = rndis_filter_set_device_mac(hdev, addr->sa_data);
362+
if (err != 0) {
363+
/* roll back to saved MAC */
364+
memcpy(ndev->dev_addr, save_adr, ETH_ALEN);
365+
ndev->addr_assign_type = save_aatype;
366+
}
367+
368+
return err;
369+
}
370+
371+
344372
static const struct ethtool_ops ethtool_ops = {
345373
.get_drvinfo = netvsc_get_drvinfo,
346374
.get_link = ethtool_op_get_link,
@@ -353,7 +381,7 @@ static const struct net_device_ops device_ops = {
353381
.ndo_set_rx_mode = netvsc_set_multicast_list,
354382
.ndo_change_mtu = netvsc_change_mtu,
355383
.ndo_validate_addr = eth_validate_addr,
356-
.ndo_set_mac_address = eth_mac_addr,
384+
.ndo_set_mac_address = netvsc_set_mac_addr,
357385
};
358386

359387
/*

drivers/net/hyperv/rndis_filter.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/if_ether.h>
2828
#include <linux/netdevice.h>
2929
#include <linux/if_vlan.h>
30+
#include <linux/nls.h>
3031

3132
#include "hyperv_net.h"
3233

@@ -47,6 +48,7 @@ struct rndis_request {
4748
struct hv_page_buffer buf;
4849
/* FIXME: We assumed a fixed size request here. */
4950
struct rndis_message request_msg;
51+
u8 ext[100];
5052
};
5153

5254
static void rndis_filter_send_completion(void *ctx);
@@ -511,6 +513,83 @@ static int rndis_filter_query_device_mac(struct rndis_device *dev)
511513
dev->hw_mac_adr, &size);
512514
}
513515

516+
#define NWADR_STR "NetworkAddress"
517+
#define NWADR_STRLEN 14
518+
519+
int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac)
520+
{
521+
struct netvsc_device *nvdev = hv_get_drvdata(hdev);
522+
struct rndis_device *rdev = nvdev->extension;
523+
struct net_device *ndev = nvdev->ndev;
524+
struct rndis_request *request;
525+
struct rndis_set_request *set;
526+
struct rndis_config_parameter_info *cpi;
527+
wchar_t *cfg_nwadr, *cfg_mac;
528+
struct rndis_set_complete *set_complete;
529+
char macstr[2*ETH_ALEN+1];
530+
u32 extlen = sizeof(struct rndis_config_parameter_info) +
531+
2*NWADR_STRLEN + 4*ETH_ALEN;
532+
int ret, t;
533+
534+
request = get_rndis_request(rdev, RNDIS_MSG_SET,
535+
RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
536+
if (!request)
537+
return -ENOMEM;
538+
539+
set = &request->request_msg.msg.set_req;
540+
set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
541+
set->info_buflen = extlen;
542+
set->info_buf_offset = sizeof(struct rndis_set_request);
543+
set->dev_vc_handle = 0;
544+
545+
cpi = (struct rndis_config_parameter_info *)((ulong)set +
546+
set->info_buf_offset);
547+
cpi->parameter_name_offset =
548+
sizeof(struct rndis_config_parameter_info);
549+
/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
550+
cpi->parameter_name_length = 2*NWADR_STRLEN;
551+
cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
552+
cpi->parameter_value_offset =
553+
cpi->parameter_name_offset + cpi->parameter_name_length;
554+
/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
555+
cpi->parameter_value_length = 4*ETH_ALEN;
556+
557+
cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
558+
cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
559+
ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
560+
cfg_nwadr, NWADR_STRLEN);
561+
if (ret < 0)
562+
goto cleanup;
563+
snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
564+
ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
565+
cfg_mac, 2*ETH_ALEN);
566+
if (ret < 0)
567+
goto cleanup;
568+
569+
ret = rndis_filter_send_request(rdev, request);
570+
if (ret != 0)
571+
goto cleanup;
572+
573+
t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
574+
if (t == 0) {
575+
netdev_err(ndev, "timeout before we got a set response...\n");
576+
/*
577+
* can't put_rndis_request, since we may still receive a
578+
* send-completion.
579+
*/
580+
return -EBUSY;
581+
} else {
582+
set_complete = &request->response_msg.msg.set_complete;
583+
if (set_complete->status != RNDIS_STATUS_SUCCESS)
584+
ret = -EINVAL;
585+
}
586+
587+
cleanup:
588+
put_rndis_request(rdev, request);
589+
return ret;
590+
}
591+
592+
514593
static int rndis_filter_query_device_link_status(struct rndis_device *dev)
515594
{
516595
u32 size = sizeof(u32);

0 commit comments

Comments
 (0)