diff --git a/xen/common/v4v.c b/xen/common/v4v.c index e589fda..381fcdd 100644 --- a/xen/common/v4v.c +++ b/xen/common/v4v.c @@ -43,6 +43,7 @@ #endif +struct list_head viprules = LIST_HEAD_INIT(viprules); DEFINE_XEN_GUEST_HANDLE (uint8_t); static struct v4v_ring_info *v4v_ring_find_info (struct domain *d, @@ -1312,7 +1313,221 @@ v4v_notify (struct domain *d, return ret; } +#ifdef V4V_DEBUG +void +v4v_viptables_print_rule (struct v4v_viptables_rule_node *rule) +{ + if (rule == NULL) + { + printk("(null)\n"); + return; + } + + if (rule->accept == 1) + printk("ACCEPT"); + else + printk("REJECT"); + + printk(" "); + + if (rule->src.domain == DOMID_INVALID) + printk("*"); + else + printk("%i", rule->src.domain); + + printk(":"); + + if (rule->src.port == -1) + printk("*"); + else + printk("%i", rule->src.port); + + printk(" -> "); + + if (rule->dst.domain == DOMID_INVALID) + printk("*"); + else + printk("%i", rule->dst.domain); + printk(":"); + + if (rule->dst.port == -1) + printk("*"); + else + printk("%i", rule->dst.port); + + printk("\n"); +} +#endif /* V4V_DEBUG */ + +int +v4v_viptables_add (struct domain *src_d, XEN_GUEST_HANDLE(v4v_viptables_rule_t) rule, + int32_t position) +{ + struct v4v_viptables_rule_node* new; + struct list_head* tmp; + + /* First rule is n.1 */ + position--; + + new = xmalloc (struct v4v_viptables_rule_node); + + if (copy_field_from_guest (new, rule, src)) + return -EFAULT; + if (copy_field_from_guest (new, rule, dst)) + return -EFAULT; + if (copy_field_from_guest (new, rule, accept)) + return -EFAULT; + +#ifdef V4V_DEBUG + printk(KERN_ERR "VIPTables: "); + v4v_viptables_print_rule(new); +#endif /* V4V_DEBUG */ + + tmp = &viprules; + while (position != 0 && tmp->next != &viprules) + { + tmp = tmp->next; + position--; + } + list_add(&new->list, tmp); + + return 0; +} + +int +v4v_viptables_del (struct domain *src_d, XEN_GUEST_HANDLE(v4v_viptables_rule_t) rule, + int32_t position) +{ + struct list_head *tmp = NULL; + struct list_head *next = NULL; + struct v4v_viptables_rule_node *node; + struct v4v_viptables_rule *r; + + if (position != -1) + { + /* We want to delete the rule number */ + tmp = &viprules; + while (position != 0 && tmp->next != &viprules) + { + tmp = tmp->next; + position--; + } + } + else if (!guest_handle_is_null(rule)) + { + /* We want to delete the rule */ + r = xmalloc (struct v4v_viptables_rule); + + if (copy_field_from_guest (r, rule, src)) + return -EFAULT; + if (copy_field_from_guest (r, rule, dst)) + return -EFAULT; + if (copy_field_from_guest (r, rule, accept)) + return -EFAULT; + + list_for_each(tmp, &viprules) + { + node = list_entry(tmp, struct v4v_viptables_rule_node, list); + + if ((node->src.domain == r->src.domain) && + (node->src.port == r->src.port) && + (node->dst.domain == r->dst.domain) && + (node->dst.port == r->dst.port)) + { + position = 0; + break; + } + } + xfree(r); + } + else + { + /* We want to flush the rules! */ + printk(KERN_ERR "VIPTables: flushing rules\n"); + list_for_each_safe(tmp, next, &viprules) + { + node = list_entry(tmp, struct v4v_viptables_rule_node, list); + list_del(tmp); + xfree(node); + } + } + +#ifdef V4V_DEBUG + if (position == 0 && tmp != &viprules) + { + printk(KERN_ERR "VIPTables: deleting rule: "); + node = list_entry(tmp, struct v4v_viptables_rule_node, list); + v4v_viptables_print_rule(node); + list_del(tmp); + xfree(node); + } +#endif /* V4V_DEBUG */ + + return 0; +} + +static size_t +v4v_viptables_list (struct domain *src_d, XEN_GUEST_HANDLE(v4v_viptables_list_t) list_hnd) +{ + struct list_head *ptr; + struct v4v_viptables_rule_node *node; + struct v4v_viptables_list rules_list; + uint32_t nbrules; + + memset(&rules_list, 0, sizeof (rules_list)); + if (copy_field_from_guest (&rules_list, list_hnd, nb_rules)) + return -EFAULT; + + ptr = viprules.next; + while (rules_list.nb_rules != 0 && ptr->next != &viprules) + { + ptr = ptr->next; + rules_list.start_rule--; + } + + if (rules_list.nb_rules != 0) + return -EFAULT; + + nbrules = 0; + while (nbrules < rules_list.nb_rules && ptr != &viprules) + { + node = list_entry(ptr, struct v4v_viptables_rule_node, list); + + rules_list.rules[rules_list.nb_rules].src = node->src; + rules_list.rules[rules_list.nb_rules].dst = node->dst; + rules_list.rules[rules_list.nb_rules].accept = node->accept; + + nbrules++; + ptr = ptr->next; + } + + if (copy_to_guest(list_hnd, &rules_list, 1)) + return -EFAULT; + + return 0; +} + +static size_t +v4v_viptables_check (v4v_addr_t * src, v4v_addr_t * dst) +{ + struct list_head *ptr; + struct v4v_viptables_rule_node *node; + + list_for_each(ptr, &viprules) + { + node = list_entry(ptr, struct v4v_viptables_rule_node, list); + + if ((node->src.domain == DOMID_INVALID || node->src.domain == src->domain) && + (node->src.port == -1 || node->src.port == src->port) && + (node->dst.domain == DOMID_INVALID || node->dst.domain == dst->domain) && + (node->dst.port == -1 || node->dst.port == dst->port)) + return !node->accept; + } + + /* Defaulting to ACCEPT */ + return 0; +} /*Hypercall to do the send*/ static size_t @@ -1351,6 +1566,15 @@ v4v_send (struct domain *src_d, v4v_addr_t * src_addr, return -ECONNREFUSED; } + if (v4v_viptables_check(src_addr, dst_addr) != 0) + { + read_unlock (&v4v_lock); + printk(KERN_ERR "V4V: VIPTables REJECTED %i:%i -> %i:%i\n", + src_addr->domain, src_addr->port, + dst_addr->domain, dst_addr->port); + return -ECONNREFUSED; + } + do { if ( !dst_d->v4v ) @@ -1437,6 +1661,15 @@ v4v_sendv (struct domain *src_d, v4v_addr_t * src_addr, return -ECONNREFUSED; } + if (v4v_viptables_check(src_addr, dst_addr) != 0) + { + read_unlock (&v4v_lock); + printk(KERN_ERR "V4V: VIPTables REJECTED %i:%i -> %i:%i\n", + src_addr->domain, src_addr->port, + dst_addr->domain, dst_addr->port); + return -ECONNREFUSED; + } + do { if ( !dst_d->v4v ) @@ -1596,6 +1829,38 @@ do_v4v_op (int cmd, XEN_GUEST_HANDLE (void) arg1, rc = v4v_notify (d, ring_data_hnd); break; } + case V4VOP_viptables_add: + { + uint32_t position = arg4; + XEN_GUEST_HANDLE (v4v_viptables_rule_t) rule_hnd = + guest_handle_cast (arg1, v4v_viptables_rule_t); + rc = -EPERM; + if (!IS_PRIV(d)) + goto out; + rc = v4v_viptables_add (d, rule_hnd, position); + break; + } + case V4VOP_viptables_del: + { + uint32_t position = arg4; + XEN_GUEST_HANDLE (v4v_viptables_rule_t) rule_hnd = + guest_handle_cast (arg1, v4v_viptables_rule_t); + rc = -EPERM; + if (!IS_PRIV(d)) + goto out; + rc = v4v_viptables_del (d, rule_hnd, position); + break; + } + case V4VOP_viptables_list: + { + XEN_GUEST_HANDLE (v4v_viptables_list_t) rules_list_hnd = + guest_handle_cast(arg1, v4v_viptables_list_t); + rc = -EPERM; + if (!IS_PRIV(d)) + goto out; + rc = v4v_viptables_list (d, rules_list_hnd); + break; + } default: rc = -ENOSYS; break; diff --git a/xen/include/public/v4v.h b/xen/include/public/v4v.h index 197770e..d8ca507 100644 --- a/xen/include/public/v4v.h +++ b/xen/include/public/v4v.h @@ -213,6 +213,9 @@ * NULL, NULL, nent, 0) */ +#define V4VOP_viptables_add 6 +#define V4VOP_viptables_del 7 +#define V4VOP_viptables_list 8 #define V4VOP_sendv 5 /* diff --git a/xen/include/xen/v4v.h b/xen/include/xen/v4v.h index 641a6a8..e5b4cb7 100644 --- a/xen/include/xen/v4v.h +++ b/xen/include/xen/v4v.h @@ -103,6 +103,32 @@ struct v4v_ring_message_header uint8_t data[0]; } V4V_PACKED; +typedef struct v4v_viptables_rule +{ + struct v4v_addr src; + struct v4v_addr dst; + uint32_t accept; +} V4V_PACKED v4v_viptables_rule_t; + +DEFINE_XEN_GUEST_HANDLE (v4v_viptables_rule_t); + +struct v4v_viptables_rule_node +{ + struct list_head list; + struct v4v_addr src; + struct v4v_addr dst; + uint32_t accept; +} V4V_PACKED; + +typedef struct v4v_viptables_list +{ + uint32_t start_rule; + uint32_t nb_rules; + struct v4v_viptables_rule rules[0]; +} V4V_PACKED v4v_viptables_list_t; + +DEFINE_XEN_GUEST_HANDLE (v4v_viptables_list_t); + /* * Helper functions */