Commit 52e5070f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hyperv-fixes-signed-20240411' of...

Merge tag 'hyperv-fixes-signed-20240411' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux

Pull hyperv fixes from Wei Liu:

 - Some cosmetic changes (Erni Sri Satya Vennela, Li Zhijian)

 - Introduce hv_numa_node_to_pxm_info() (Nuno Das Neves)

 - Fix KVP daemon to handle IPv4 and IPv6 combination for keyfile format
   (Shradha Gupta)

 - Avoid freeing decrypted memory in a confidential VM (Rick Edgecombe
   and Michael Kelley)

* tag 'hyperv-fixes-signed-20240411' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux:
  Drivers: hv: vmbus: Don't free ring buffers that couldn't be re-encrypted
  uio_hv_generic: Don't free decrypted memory
  hv_netvsc: Don't free decrypted memory
  Drivers: hv: vmbus: Track decrypted status in vmbus_gpadl
  Drivers: hv: vmbus: Leak pages if set_memory_encrypted() fails
  hv/hv_kvp_daemon: Handle IPv4 and Ipv6 combination for keyfile format
  hv: vmbus: Convert sprintf() family to sysfs_emit() family
  mshyperv: Introduce hv_numa_node_to_pxm_info()
  x86/hyperv: Cosmetic changes for hv_apic.c
parents 00dcf5d8 30d18df6
......@@ -132,7 +132,7 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector,
if (!cpumask_equal(mask, cpu_present_mask) || exclude_self) {
ipi_arg->vp_set.format = HV_GENERIC_SET_SPARSE_4K;
nr_bank = cpumask_to_vpset_skip(&(ipi_arg->vp_set), mask,
nr_bank = cpumask_to_vpset_skip(&ipi_arg->vp_set, mask,
exclude_self ? cpu_is_self : NULL);
/*
......@@ -181,7 +181,7 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector,
return false;
}
if ((vector < HV_IPI_LOW_VECTOR) || (vector > HV_IPI_HIGH_VECTOR))
if (vector < HV_IPI_LOW_VECTOR || vector > HV_IPI_HIGH_VECTOR)
return false;
/*
......@@ -241,7 +241,7 @@ static bool __send_ipi_one(int cpu, int vector)
return false;
}
if ((vector < HV_IPI_LOW_VECTOR) || (vector > HV_IPI_HIGH_VECTOR))
if (vector < HV_IPI_LOW_VECTOR || vector > HV_IPI_HIGH_VECTOR)
return false;
if (vp >= 64)
......
......@@ -3,7 +3,6 @@
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/clockchips.h>
#include <linux/acpi.h>
#include <linux/hyperv.h>
#include <linux/slab.h>
#include <linux/cpuhotplug.h>
......@@ -116,12 +115,11 @@ int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)
{
struct hv_add_logical_processor_in *input;
struct hv_add_logical_processor_out *output;
struct hv_input_add_logical_processor *input;
struct hv_output_add_logical_processor *output;
u64 status;
unsigned long flags;
int ret = HV_STATUS_SUCCESS;
int pxm = node_to_pxm(node);
/*
* When adding a logical processor, the hypervisor may return
......@@ -137,11 +135,7 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)
input->lp_index = lp_index;
input->apic_id = apic_id;
input->flags = 0;
input->proximity_domain_info.domain_id = pxm;
input->proximity_domain_info.flags.reserved = 0;
input->proximity_domain_info.flags.proximity_info_valid = 1;
input->proximity_domain_info.flags.proximity_preferred = 1;
input->proximity_domain_info = hv_numa_node_to_pxm_info(node);
status = hv_do_hypercall(HVCALL_ADD_LOGICAL_PROCESSOR,
input, output);
local_irq_restore(flags);
......@@ -166,7 +160,6 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)
u64 status;
unsigned long irq_flags;
int ret = HV_STATUS_SUCCESS;
int pxm = node_to_pxm(node);
/* Root VPs don't seem to need pages deposited */
if (partition_id != hv_current_partition_id) {
......@@ -185,14 +178,7 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)
input->vp_index = vp_index;
input->flags = flags;
input->subnode_type = HvSubnodeAny;
if (node != NUMA_NO_NODE) {
input->proximity_domain_info.domain_id = pxm;
input->proximity_domain_info.flags.reserved = 0;
input->proximity_domain_info.flags.proximity_info_valid = 1;
input->proximity_domain_info.flags.proximity_preferred = 1;
} else {
input->proximity_domain_info.as_uint64 = 0;
}
input->proximity_domain_info = hv_numa_node_to_pxm_info(node);
status = hv_do_hypercall(HVCALL_CREATE_VP, input, NULL);
local_irq_restore(irq_flags);
......
......@@ -153,6 +153,8 @@ void vmbus_free_ring(struct vmbus_channel *channel)
hv_ringbuffer_cleanup(&channel->inbound);
if (channel->ringbuffer_page) {
/* In a CoCo VM leak the memory if it didn't get re-encrypted */
if (!channel->ringbuffer_gpadlhandle.decrypted)
__free_pages(channel->ringbuffer_page,
get_order(channel->ringbuffer_pagecount
<< PAGE_SHIFT));
......@@ -436,9 +438,18 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
(atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
ret = create_gpadl_header(type, kbuffer, size, send_offset, &msginfo);
if (ret)
if (ret) {
gpadl->decrypted = false;
return ret;
}
/*
* Set the "decrypted" flag to true for the set_memory_decrypted()
* success case. In the failure case, the encryption state of the
* memory is unknown. Leave "decrypted" as true to ensure the
* memory will be leaked instead of going back on the free list.
*/
gpadl->decrypted = true;
ret = set_memory_decrypted((unsigned long)kbuffer,
PFN_UP(size));
if (ret) {
......@@ -527,9 +538,15 @@ static int __vmbus_establish_gpadl(struct vmbus_channel *channel,
kfree(msginfo);
if (ret)
set_memory_encrypted((unsigned long)kbuffer,
PFN_UP(size));
if (ret) {
/*
* If set_memory_encrypted() fails, the decrypted flag is
* left as true so the memory is leaked instead of being
* put back on the free list.
*/
if (!set_memory_encrypted((unsigned long)kbuffer, PFN_UP(size)))
gpadl->decrypted = false;
}
return ret;
}
......@@ -850,6 +867,8 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, struct vmbus_gpadl *gpad
if (ret)
pr_warn("Fail to set mem host visibility in GPADL teardown %d.\n", ret);
gpadl->decrypted = ret;
return ret;
}
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
......
......@@ -237,8 +237,17 @@ int vmbus_connect(void)
vmbus_connection.monitor_pages[0], 1);
ret |= set_memory_decrypted((unsigned long)
vmbus_connection.monitor_pages[1], 1);
if (ret)
if (ret) {
/*
* If set_memory_decrypted() fails, the encryption state
* of the memory is unknown. So leak the memory instead
* of risking returning decrypted memory to the free list.
* For simplicity, always handle both pages the same.
*/
vmbus_connection.monitor_pages[0] = NULL;
vmbus_connection.monitor_pages[1] = NULL;
goto cleanup;
}
/*
* Set_memory_decrypted() will change the memory contents if
......@@ -337,13 +346,19 @@ void vmbus_disconnect(void)
vmbus_connection.int_page = NULL;
}
set_memory_encrypted((unsigned long)vmbus_connection.monitor_pages[0], 1);
set_memory_encrypted((unsigned long)vmbus_connection.monitor_pages[1], 1);
if (vmbus_connection.monitor_pages[0]) {
if (!set_memory_encrypted(
(unsigned long)vmbus_connection.monitor_pages[0], 1))
hv_free_hyperv_page(vmbus_connection.monitor_pages[0]);
hv_free_hyperv_page(vmbus_connection.monitor_pages[1]);
vmbus_connection.monitor_pages[0] = NULL;
}
if (vmbus_connection.monitor_pages[1]) {
if (!set_memory_encrypted(
(unsigned long)vmbus_connection.monitor_pages[1], 1))
hv_free_hyperv_page(vmbus_connection.monitor_pages[1]);
vmbus_connection.monitor_pages[1] = NULL;
}
}
/*
......
......@@ -131,7 +131,7 @@ static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid);
return sysfs_emit(buf, "%d\n", hv_dev->channel->offermsg.child_relid);
}
static DEVICE_ATTR_RO(id);
......@@ -142,7 +142,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *dev_attr,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n", hv_dev->channel->state);
return sysfs_emit(buf, "%d\n", hv_dev->channel->state);
}
static DEVICE_ATTR_RO(state);
......@@ -153,7 +153,7 @@ static ssize_t monitor_id_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n", hv_dev->channel->offermsg.monitorid);
return sysfs_emit(buf, "%d\n", hv_dev->channel->offermsg.monitorid);
}
static DEVICE_ATTR_RO(monitor_id);
......@@ -164,7 +164,7 @@ static ssize_t class_id_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "{%pUl}\n",
return sysfs_emit(buf, "{%pUl}\n",
&hv_dev->channel->offermsg.offer.if_type);
}
static DEVICE_ATTR_RO(class_id);
......@@ -176,7 +176,7 @@ static ssize_t device_id_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "{%pUl}\n",
return sysfs_emit(buf, "{%pUl}\n",
&hv_dev->channel->offermsg.offer.if_instance);
}
static DEVICE_ATTR_RO(device_id);
......@@ -186,7 +186,7 @@ static ssize_t modalias_show(struct device *dev,
{
struct hv_device *hv_dev = device_to_hv_device(dev);
return sprintf(buf, "vmbus:%*phN\n", UUID_SIZE, &hv_dev->dev_type);
return sysfs_emit(buf, "vmbus:%*phN\n", UUID_SIZE, &hv_dev->dev_type);
}
static DEVICE_ATTR_RO(modalias);
......@@ -199,7 +199,7 @@ static ssize_t numa_node_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n", cpu_to_node(hv_dev->channel->target_cpu));
return sysfs_emit(buf, "%d\n", cpu_to_node(hv_dev->channel->target_cpu));
}
static DEVICE_ATTR_RO(numa_node);
#endif
......@@ -212,8 +212,7 @@ static ssize_t server_monitor_pending_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n",
channel_pending(hv_dev->channel,
return sysfs_emit(buf, "%d\n", channel_pending(hv_dev->channel,
vmbus_connection.monitor_pages[0]));
}
static DEVICE_ATTR_RO(server_monitor_pending);
......@@ -226,8 +225,7 @@ static ssize_t client_monitor_pending_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n",
channel_pending(hv_dev->channel,
return sysfs_emit(buf, "%d\n", channel_pending(hv_dev->channel,
vmbus_connection.monitor_pages[1]));
}
static DEVICE_ATTR_RO(client_monitor_pending);
......@@ -240,8 +238,7 @@ static ssize_t server_monitor_latency_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n",
channel_latency(hv_dev->channel,
return sysfs_emit(buf, "%d\n", channel_latency(hv_dev->channel,
vmbus_connection.monitor_pages[0]));
}
static DEVICE_ATTR_RO(server_monitor_latency);
......@@ -254,8 +251,7 @@ static ssize_t client_monitor_latency_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n",
channel_latency(hv_dev->channel,
return sysfs_emit(buf, "%d\n", channel_latency(hv_dev->channel,
vmbus_connection.monitor_pages[1]));
}
static DEVICE_ATTR_RO(client_monitor_latency);
......@@ -268,8 +264,7 @@ static ssize_t server_monitor_conn_id_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n",
channel_conn_id(hv_dev->channel,
return sysfs_emit(buf, "%d\n", channel_conn_id(hv_dev->channel,
vmbus_connection.monitor_pages[0]));
}
static DEVICE_ATTR_RO(server_monitor_conn_id);
......@@ -282,8 +277,7 @@ static ssize_t client_monitor_conn_id_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "%d\n",
channel_conn_id(hv_dev->channel,
return sysfs_emit(buf, "%d\n", channel_conn_id(hv_dev->channel,
vmbus_connection.monitor_pages[1]));
}
static DEVICE_ATTR_RO(client_monitor_conn_id);
......@@ -303,7 +297,7 @@ static ssize_t out_intr_mask_show(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", outbound.current_interrupt_mask);
return sysfs_emit(buf, "%d\n", outbound.current_interrupt_mask);
}
static DEVICE_ATTR_RO(out_intr_mask);
......@@ -321,7 +315,7 @@ static ssize_t out_read_index_show(struct device *dev,
&outbound);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", outbound.current_read_index);
return sysfs_emit(buf, "%d\n", outbound.current_read_index);
}
static DEVICE_ATTR_RO(out_read_index);
......@@ -340,7 +334,7 @@ static ssize_t out_write_index_show(struct device *dev,
&outbound);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", outbound.current_write_index);
return sysfs_emit(buf, "%d\n", outbound.current_write_index);
}
static DEVICE_ATTR_RO(out_write_index);
......@@ -359,7 +353,7 @@ static ssize_t out_read_bytes_avail_show(struct device *dev,
&outbound);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", outbound.bytes_avail_toread);
return sysfs_emit(buf, "%d\n", outbound.bytes_avail_toread);
}
static DEVICE_ATTR_RO(out_read_bytes_avail);
......@@ -378,7 +372,7 @@ static ssize_t out_write_bytes_avail_show(struct device *dev,
&outbound);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", outbound.bytes_avail_towrite);
return sysfs_emit(buf, "%d\n", outbound.bytes_avail_towrite);
}
static DEVICE_ATTR_RO(out_write_bytes_avail);
......@@ -396,7 +390,7 @@ static ssize_t in_intr_mask_show(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", inbound.current_interrupt_mask);
return sysfs_emit(buf, "%d\n", inbound.current_interrupt_mask);
}
static DEVICE_ATTR_RO(in_intr_mask);
......@@ -414,7 +408,7 @@ static ssize_t in_read_index_show(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", inbound.current_read_index);
return sysfs_emit(buf, "%d\n", inbound.current_read_index);
}
static DEVICE_ATTR_RO(in_read_index);
......@@ -432,7 +426,7 @@ static ssize_t in_write_index_show(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", inbound.current_write_index);
return sysfs_emit(buf, "%d\n", inbound.current_write_index);
}
static DEVICE_ATTR_RO(in_write_index);
......@@ -451,7 +445,7 @@ static ssize_t in_read_bytes_avail_show(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", inbound.bytes_avail_toread);
return sysfs_emit(buf, "%d\n", inbound.bytes_avail_toread);
}
static DEVICE_ATTR_RO(in_read_bytes_avail);
......@@ -470,7 +464,7 @@ static ssize_t in_write_bytes_avail_show(struct device *dev,
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", inbound.bytes_avail_towrite);
return sysfs_emit(buf, "%d\n", inbound.bytes_avail_towrite);
}
static DEVICE_ATTR_RO(in_write_bytes_avail);
......@@ -480,7 +474,7 @@ static ssize_t channel_vp_mapping_show(struct device *dev,
{
struct hv_device *hv_dev = device_to_hv_device(dev);
struct vmbus_channel *channel = hv_dev->channel, *cur_sc;
int buf_size = PAGE_SIZE, n_written, tot_written;
int n_written;
struct list_head *cur;
if (!channel)
......@@ -488,25 +482,21 @@ static ssize_t channel_vp_mapping_show(struct device *dev,
mutex_lock(&vmbus_connection.channel_mutex);
tot_written = snprintf(buf, buf_size, "%u:%u\n",
channel->offermsg.child_relid, channel->target_cpu);
n_written = sysfs_emit(buf, "%u:%u\n",
channel->offermsg.child_relid,
channel->target_cpu);
list_for_each(cur, &channel->sc_list) {
if (tot_written >= buf_size - 1)
break;
cur_sc = list_entry(cur, struct vmbus_channel, sc_list);
n_written = scnprintf(buf + tot_written,
buf_size - tot_written,
"%u:%u\n",
n_written += sysfs_emit_at(buf, n_written, "%u:%u\n",
cur_sc->offermsg.child_relid,
cur_sc->target_cpu);
tot_written += n_written;
}
mutex_unlock(&vmbus_connection.channel_mutex);
return tot_written;
return n_written;
}
static DEVICE_ATTR_RO(channel_vp_mapping);
......@@ -516,7 +506,7 @@ static ssize_t vendor_show(struct device *dev,
{
struct hv_device *hv_dev = device_to_hv_device(dev);
return sprintf(buf, "0x%x\n", hv_dev->vendor_id);
return sysfs_emit(buf, "0x%x\n", hv_dev->vendor_id);
}
static DEVICE_ATTR_RO(vendor);
......@@ -526,7 +516,7 @@ static ssize_t device_show(struct device *dev,
{
struct hv_device *hv_dev = device_to_hv_device(dev);
return sprintf(buf, "0x%x\n", hv_dev->device_id);
return sysfs_emit(buf, "0x%x\n", hv_dev->device_id);
}
static DEVICE_ATTR_RO(device);
......@@ -551,7 +541,7 @@ static ssize_t driver_override_show(struct device *dev,
ssize_t len;
device_lock(dev);
len = snprintf(buf, PAGE_SIZE, "%s\n", hv_dev->driver_override);
len = sysfs_emit(buf, "%s\n", hv_dev->driver_override);
device_unlock(dev);
return len;
......
......@@ -154,7 +154,10 @@ static void free_netvsc_device(struct rcu_head *head)
int i;
kfree(nvdev->extension);
if (!nvdev->recv_buf_gpadl_handle.decrypted)
vfree(nvdev->recv_buf);
if (!nvdev->send_buf_gpadl_handle.decrypted)
vfree(nvdev->send_buf);
bitmap_free(nvdev->send_section_map);
......
......@@ -181,11 +181,13 @@ hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata)
{
if (pdata->send_gpadl.gpadl_handle) {
vmbus_teardown_gpadl(dev->channel, &pdata->send_gpadl);
if (!pdata->send_gpadl.decrypted)
vfree(pdata->send_buf);
}
if (pdata->recv_gpadl.gpadl_handle) {
vmbus_teardown_gpadl(dev->channel, &pdata->recv_gpadl);
if (!pdata->recv_gpadl.decrypted)
vfree(pdata->recv_buf);
}
}
......@@ -295,6 +297,7 @@ hv_uio_probe(struct hv_device *dev,
ret = vmbus_establish_gpadl(channel, pdata->recv_buf,
RECV_BUFFER_SIZE, &pdata->recv_gpadl);
if (ret) {
if (!pdata->recv_gpadl.decrypted)
vfree(pdata->recv_buf);
goto fail_close;
}
......@@ -317,6 +320,7 @@ hv_uio_probe(struct hv_device *dev,
ret = vmbus_establish_gpadl(channel, pdata->send_buf,
SEND_BUFFER_SIZE, &pdata->send_gpadl);
if (ret) {
if (!pdata->send_gpadl.decrypted)
vfree(pdata->send_buf);
goto fail_close;
}
......
......@@ -512,13 +512,9 @@ struct hv_proximity_domain_flags {
u32 proximity_info_valid : 1;
} __packed;
/* Not a union in windows but useful for zeroing */
union hv_proximity_domain_info {
struct {
struct hv_proximity_domain_info {
u32 domain_id;
struct hv_proximity_domain_flags flags;
};
u64 as_uint64;
} __packed;
struct hv_lp_startup_status {
......@@ -532,14 +528,13 @@ struct hv_lp_startup_status {
} __packed;
/* HvAddLogicalProcessor hypercall */
struct hv_add_logical_processor_in {
struct hv_input_add_logical_processor {
u32 lp_index;
u32 apic_id;
union hv_proximity_domain_info proximity_domain_info;
u64 flags;
struct hv_proximity_domain_info proximity_domain_info;
} __packed;
struct hv_add_logical_processor_out {
struct hv_output_add_logical_processor {
struct hv_lp_startup_status startup_status;
} __packed;
......@@ -560,7 +555,7 @@ struct hv_create_vp {
u8 padding[3];
u8 subnode_type;
u64 subnode_id;
union hv_proximity_domain_info proximity_domain_info;
struct hv_proximity_domain_info proximity_domain_info;
u64 flags;
} __packed;
......
......@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <acpi/acpi_numa.h>
#include <linux/cpumask.h>
#include <linux/nmi.h>
#include <asm/ptrace.h>
......@@ -67,6 +68,19 @@ extern u64 hv_do_fast_hypercall8(u16 control, u64 input8);
bool hv_isolation_type_snp(void);
bool hv_isolation_type_tdx(void);
static inline struct hv_proximity_domain_info hv_numa_node_to_pxm_info(int node)
{
struct hv_proximity_domain_info pxm_info = {};
if (node != NUMA_NO_NODE) {
pxm_info.domain_id = node_to_pxm(node);
pxm_info.flags.proximity_info_valid = 1;
pxm_info.flags.proximity_preferred = 1;
}
return pxm_info;
}
/* Helper functions that provide a consistent pattern for checking Hyper-V hypercall status. */
static inline int hv_result(u64 status)
{
......
......@@ -832,6 +832,7 @@ struct vmbus_gpadl {
u32 gpadl_handle;
u32 size;
void *buffer;
bool decrypted;
};
struct vmbus_channel {
......
......@@ -76,6 +76,12 @@ enum {
DNS
};
enum {
IPV4 = 1,
IPV6,
IP_TYPE_MAX
};
static int in_hand_shake;
static char *os_name = "";
......@@ -102,6 +108,11 @@ static struct utsname uts_buf;
#define MAX_FILE_NAME 100
#define ENTRIES_PER_BLOCK 50
/*
* Change this entry if the number of addresses increases in future
*/
#define MAX_IP_ENTRIES 64
#define OUTSTR_BUF_SIZE ((INET6_ADDRSTRLEN + 1) * MAX_IP_ENTRIES)
struct kvp_record {
char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
......@@ -1171,6 +1182,18 @@ static int process_ip_string(FILE *f, char *ip_string, int type)
return 0;
}
int ip_version_check(const char *input_addr)
{
struct in6_addr addr;
if (inet_pton(AF_INET, input_addr, &addr))
return IPV4;
else if (inet_pton(AF_INET6, input_addr, &addr))
return IPV6;
return -EINVAL;
}
/*
* Only IPv4 subnet strings needs to be converted to plen
* For IPv6 the subnet is already privided in plen format
......@@ -1197,14 +1220,75 @@ static int kvp_subnet_to_plen(char *subnet_addr_str)
return plen;
}
static int process_dns_gateway_nm(FILE *f, char *ip_string, int type,
int ip_sec)
{
char addr[INET6_ADDRSTRLEN], *output_str;
int ip_offset = 0, error = 0, ip_ver;
char *param_name;
if (type == DNS)
param_name = "dns";
else if (type == GATEWAY)
param_name = "gateway";
else
return -EINVAL;
output_str = (char *)calloc(OUTSTR_BUF_SIZE, sizeof(char));
if (!output_str)
return -ENOMEM;
while (1) {
memset(addr, 0, sizeof(addr));
if (!parse_ip_val_buffer(ip_string, &ip_offset, addr,
(MAX_IP_ADDR_SIZE * 2)))
break;
ip_ver = ip_version_check(addr);
if (ip_ver < 0)
continue;
if ((ip_ver == IPV4 && ip_sec == IPV4) ||
(ip_ver == IPV6 && ip_sec == IPV6)) {
/*
* do a bound check to avoid out-of bound writes
*/
if ((OUTSTR_BUF_SIZE - strlen(output_str)) >
(strlen(addr) + 1)) {
strncat(output_str, addr,
OUTSTR_BUF_SIZE -
strlen(output_str) - 1);
strncat(output_str, ",",
OUTSTR_BUF_SIZE -
strlen(output_str) - 1);
}
} else {
continue;
}
}
if (strlen(output_str)) {
/*
* This is to get rid of that extra comma character
* in the end of the string
*/
output_str[strlen(output_str) - 1] = '\0';
error = fprintf(f, "%s=%s\n", param_name, output_str);
}
free(output_str);
return error;
}
static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
int is_ipv6)
int ip_sec)
{
char addr[INET6_ADDRSTRLEN];
char subnet_addr[INET6_ADDRSTRLEN];
int error, i = 0;
int error = 0, i = 0;
int ip_offset = 0, subnet_offset = 0;
int plen;
int plen, ip_ver;
memset(addr, 0, sizeof(addr));
memset(subnet_addr, 0, sizeof(subnet_addr));
......@@ -1216,10 +1300,16 @@ static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
subnet_addr,
(MAX_IP_ADDR_SIZE *
2))) {
if (!is_ipv6)
ip_ver = ip_version_check(addr);
if (ip_ver < 0)
continue;
if (ip_ver == IPV4 && ip_sec == IPV4)
plen = kvp_subnet_to_plen((char *)subnet_addr);
else
else if (ip_ver == IPV6 && ip_sec == IPV6)
plen = atoi(subnet_addr);
else
continue;
if (plen < 0)
return plen;
......@@ -1233,17 +1323,16 @@ static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet,
memset(subnet_addr, 0, sizeof(subnet_addr));
}
return 0;
return error;
}
static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
{
int error = 0;
int error = 0, ip_ver;
char if_filename[PATH_MAX];
char nm_filename[PATH_MAX];
FILE *ifcfg_file, *nmfile;
char cmd[PATH_MAX];
int is_ipv6 = 0;
char *mac_addr;
int str_len;
......@@ -1421,52 +1510,94 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
if (error)
goto setval_error;
if (new_val->addr_family & ADDR_FAMILY_IPV6) {
error = fprintf(nmfile, "\n[ipv6]\n");
/*
* Now we populate the keyfile format
*
* The keyfile format expects the IPv6 and IPv4 configuration in
* different sections. Therefore we iterate through the list twice,
* once to populate the IPv4 section and the next time for IPv6
*/
ip_ver = IPV4;
do {
if (ip_ver == IPV4) {
error = fprintf(nmfile, "\n[ipv4]\n");
if (error < 0)
goto setval_error;
is_ipv6 = 1;
} else {
error = fprintf(nmfile, "\n[ipv4]\n");
error = fprintf(nmfile, "\n[ipv6]\n");
if (error < 0)
goto setval_error;
}
/*
* Now we populate the keyfile format
* Write the configuration for ipaddress, netmask, gateway and
* name services
*/
error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr,
(char *)new_val->sub_net,
ip_ver);
if (error < 0)
goto setval_error;
/*
* As dhcp_enabled is only valid for ipv4, we do not set dhcp
* methods for ipv6 based on dhcp_enabled flag.
*
* For ipv4, set method to manual only when dhcp_enabled is
* false and specific ipv4 addresses are configured. If neither
* dhcp_enabled is true and no ipv4 addresses are configured,
* set method to 'disabled'.
*
* For ipv6, set method to manual when we configure ipv6
* addresses. Otherwise set method to 'auto' so that SLAAC from
* RA may be used.
*/
if (ip_ver == IPV4) {
if (new_val->dhcp_enabled) {
error = kvp_write_file(nmfile, "method", "", "auto");
error = kvp_write_file(nmfile, "method", "",
"auto");
if (error < 0)
goto setval_error;
} else if (error) {
error = kvp_write_file(nmfile, "method", "",
"manual");
if (error < 0)
goto setval_error;
} else {
error = kvp_write_file(nmfile, "method", "", "manual");
error = kvp_write_file(nmfile, "method", "",
"disabled");
if (error < 0)
goto setval_error;
}
/*
* Write the configuration for ipaddress, netmask, gateway and
* name services
*/
error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr,
(char *)new_val->sub_net, is_ipv6);
} else if (ip_ver == IPV6) {
if (error) {
error = kvp_write_file(nmfile, "method", "",
"manual");
if (error < 0)
goto setval_error;
/* we do not want ipv4 addresses in ipv6 section and vice versa */
if (is_ipv6 != is_ipv4((char *)new_val->gate_way)) {
error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way);
} else {
error = kvp_write_file(nmfile, "method", "",
"auto");
if (error < 0)
goto setval_error;
}
}
if (is_ipv6 != is_ipv4((char *)new_val->dns_addr)) {
error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr);
error = process_dns_gateway_nm(nmfile,
(char *)new_val->gate_way,
GATEWAY, ip_ver);
if (error < 0)
goto setval_error;
}
error = process_dns_gateway_nm(nmfile,
(char *)new_val->dns_addr, DNS,
ip_ver);
if (error < 0)
goto setval_error;
ip_ver++;
} while (ip_ver < IP_TYPE_MAX);
fclose(nmfile);
fclose(ifcfg_file);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment