mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 18:07:40 +00:00
Implement set-field for IPv6 ND fields (nd_target, nd_sll, and nd_tll).
This patch adds set-field operations for nd_target, nd_sll, and nd_tll fields, with and without masks, using Nicira extensions and OpenFlow 1.2 protocol. Signed-off-by: Randall A Sharo <randall.sharo at navy.mil> Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
parent
ed5c51108c
commit
e60e935b1f
1
AUTHORS
1
AUTHORS
@ -124,6 +124,7 @@ pritesh pritesh.kothari@cisco.com
|
|||||||
Pravin B Shelar pshelar@nicira.com
|
Pravin B Shelar pshelar@nicira.com
|
||||||
Raju Subramanian rsubramanian@nicira.com
|
Raju Subramanian rsubramanian@nicira.com
|
||||||
Rami Rosen ramirose@gmail.com
|
Rami Rosen ramirose@gmail.com
|
||||||
|
Randall Sharo andall.sharo@navy.mil
|
||||||
Ravi Kerur Ravi.Kerur@telekom.com
|
Ravi Kerur Ravi.Kerur@telekom.com
|
||||||
Reid Price reid@nicira.com
|
Reid Price reid@nicira.com
|
||||||
Remko Tronçon git@el-tramo.be
|
Remko Tronçon git@el-tramo.be
|
||||||
|
2
NEWS
2
NEWS
@ -22,7 +22,7 @@ Post-v2.3.0
|
|||||||
commands are now redundant and will be removed in a future
|
commands are now redundant and will be removed in a future
|
||||||
release. See ovs-vswitchd(8) for details.
|
release. See ovs-vswitchd(8) for details.
|
||||||
- OpenFlow:
|
- OpenFlow:
|
||||||
* The IPv6 flow label field is now modifiable.
|
* IPv6 flow label and neighbor discovery fields are now modifiable.
|
||||||
* OpenFlow 1.5 (draft) extended registers are now supported.
|
* OpenFlow 1.5 (draft) extended registers are now supported.
|
||||||
* The OpenFlow 1.5 (draft) actset_output field is now supported.
|
* The OpenFlow 1.5 (draft) actset_output field is now supported.
|
||||||
* OpenFlow 1.5 (draft) Copy-Field action is now supported.
|
* OpenFlow 1.5 (draft) Copy-Field action is now supported.
|
||||||
|
20
lib/csum.c
20
lib/csum.c
@ -112,6 +112,26 @@ recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32)
|
|||||||
old_u32 >> 16, new_u32 >> 16);
|
old_u32 >> 16, new_u32 >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the new checksum for a packet in which the checksum field previously
|
||||||
|
* contained 'old_csum' and in which a field that contained the 6 bytes at
|
||||||
|
* 'old_bytes' was changed to contain the 6 bytes at 'new_bytes'. */
|
||||||
|
ovs_be16
|
||||||
|
recalc_csum48(ovs_be16 old_csum, const void *old_bytes,
|
||||||
|
const void *new_bytes)
|
||||||
|
{
|
||||||
|
ovs_be16 new_csum = old_csum;
|
||||||
|
const uint16_t *p16_old = old_bytes,
|
||||||
|
*p16_new = new_bytes;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 3; ++i) {
|
||||||
|
new_csum = recalc_csum16(new_csum, get_unaligned_be16(&p16_old[i]),
|
||||||
|
get_unaligned_be16(&p16_new[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_csum;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the new checksum for a packet in which the checksum field previously
|
/* Returns the new checksum for a packet in which the checksum field previously
|
||||||
* contained 'old_csum' and in which a field that contained 'old_u32[4]' was
|
* contained 'old_csum' and in which a field that contained 'old_u32[4]' was
|
||||||
* changed to contain 'new_u32[4]'. */
|
* changed to contain 'new_u32[4]'. */
|
||||||
|
@ -28,6 +28,8 @@ uint32_t csum_continue(uint32_t partial, const void *, size_t);
|
|||||||
ovs_be16 csum_finish(uint32_t partial);
|
ovs_be16 csum_finish(uint32_t partial);
|
||||||
ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16);
|
ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16);
|
||||||
ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32);
|
ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32);
|
||||||
|
ovs_be16 recalc_csum48(ovs_be16 old_csum, const void *old_bytes,
|
||||||
|
const void *new_bytes);
|
||||||
ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4],
|
ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4],
|
||||||
const ovs_be32 new_u32[4]);
|
const ovs_be32 new_u32[4]);
|
||||||
|
|
||||||
|
@ -1343,7 +1343,7 @@ enum OVS_PACKED_ENUM mf_field_id {
|
|||||||
* Maskable: bitwise.
|
* Maskable: bitwise.
|
||||||
* Formatting: IPv6.
|
* Formatting: IPv6.
|
||||||
* Prerequisites: ND.
|
* Prerequisites: ND.
|
||||||
* Access: read-only.
|
* Access: read/write.
|
||||||
* NXM: NXM_NX_ND_TARGET(23) since v1.1.
|
* NXM: NXM_NX_ND_TARGET(23) since v1.1.
|
||||||
* OXM: OXM_OF_IPV6_ND_TARGET(31) since OF1.2 and v1.7.
|
* OXM: OXM_OF_IPV6_ND_TARGET(31) since OF1.2 and v1.7.
|
||||||
*/
|
*/
|
||||||
@ -1357,7 +1357,7 @@ enum OVS_PACKED_ENUM mf_field_id {
|
|||||||
* Maskable: bitwise.
|
* Maskable: bitwise.
|
||||||
* Formatting: Ethernet.
|
* Formatting: Ethernet.
|
||||||
* Prerequisites: ND solicit.
|
* Prerequisites: ND solicit.
|
||||||
* Access: read-only.
|
* Access: read/write.
|
||||||
* NXM: NXM_NX_ND_SLL(24) since v1.1.
|
* NXM: NXM_NX_ND_SLL(24) since v1.1.
|
||||||
* OXM: OXM_OF_IPV6_ND_SLL(32) since OF1.2 and v1.7.
|
* OXM: OXM_OF_IPV6_ND_SLL(32) since OF1.2 and v1.7.
|
||||||
*/
|
*/
|
||||||
@ -1371,7 +1371,7 @@ enum OVS_PACKED_ENUM mf_field_id {
|
|||||||
* Maskable: bitwise.
|
* Maskable: bitwise.
|
||||||
* Formatting: Ethernet.
|
* Formatting: Ethernet.
|
||||||
* Prerequisites: ND advert.
|
* Prerequisites: ND advert.
|
||||||
* Access: read-only.
|
* Access: read/write.
|
||||||
* NXM: NXM_NX_ND_TLL(25) since v1.1.
|
* NXM: NXM_NX_ND_TLL(25) since v1.1.
|
||||||
* OXM: OXM_OF_IPV6_ND_TLL(33) since OF1.2 and v1.7.
|
* OXM: OXM_OF_IPV6_ND_TLL(33) since OF1.2 and v1.7.
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
|
||||||
* Copyright (c) 2013 Simon Horman
|
* Copyright (c) 2013 Simon Horman
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -19,6 +19,7 @@
|
|||||||
#include "odp-execute.h"
|
#include "odp-execute.h"
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/icmp6.h>
|
||||||
#include <netinet/ip6.h>
|
#include <netinet/ip6.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -181,6 +182,48 @@ set_arp(struct ofpbuf *packet, const struct ovs_key_arp *key,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
odp_set_nd(struct ofpbuf *packet, const struct ovs_key_nd *key,
|
||||||
|
const struct ovs_key_nd *mask)
|
||||||
|
{
|
||||||
|
const struct ovs_nd_msg *ns = ofpbuf_l4(packet);
|
||||||
|
const struct ovs_nd_opt *nd_opt = ofpbuf_get_nd_payload(packet);
|
||||||
|
|
||||||
|
if (OVS_LIKELY(ns && nd_opt)) {
|
||||||
|
int bytes_remain = ofpbuf_l4_size(packet) - sizeof(*ns);
|
||||||
|
ovs_be32 tgt_buf[4];
|
||||||
|
uint8_t sll_buf[ETH_ADDR_LEN] = {0};
|
||||||
|
uint8_t tll_buf[ETH_ADDR_LEN] = {0};
|
||||||
|
|
||||||
|
while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) {
|
||||||
|
if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
|
||||||
|
&& nd_opt->nd_opt_len == 1) {
|
||||||
|
memcpy(sll_buf, nd_opt->nd_opt_data, ETH_ADDR_LEN);
|
||||||
|
ether_addr_copy_masked(sll_buf, key->nd_sll, mask->nd_sll);
|
||||||
|
|
||||||
|
/* A packet can only contain one SLL or TLL option */
|
||||||
|
break;
|
||||||
|
} else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
|
||||||
|
&& nd_opt->nd_opt_len == 1) {
|
||||||
|
memcpy(tll_buf, nd_opt->nd_opt_data, ETH_ADDR_LEN);
|
||||||
|
ether_addr_copy_masked(tll_buf, key->nd_tll, mask->nd_tll);
|
||||||
|
|
||||||
|
/* A packet can only contain one SLL or TLL option */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nd_opt += nd_opt->nd_opt_len;
|
||||||
|
bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_set_nd(packet,
|
||||||
|
mask_ipv6_addr(ns->target.be32,
|
||||||
|
key->nd_target, mask->nd_target, tgt_buf),
|
||||||
|
sll_buf,
|
||||||
|
tll_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
|
odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
|
||||||
{
|
{
|
||||||
@ -259,6 +302,15 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
|
|||||||
set_arp(&packet->ofpbuf, nl_attr_get(a), NULL);
|
set_arp(&packet->ofpbuf, nl_attr_get(a), NULL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OVS_KEY_ATTR_ND:
|
||||||
|
if (OVS_LIKELY(ofpbuf_get_nd_payload(&packet->ofpbuf))) {
|
||||||
|
const struct ovs_key_nd *nd_key
|
||||||
|
= nl_attr_get_unspec(a, sizeof(struct ovs_key_nd));
|
||||||
|
packet_set_nd(&packet->ofpbuf, nd_key->nd_target,
|
||||||
|
nd_key->nd_sll, nd_key->nd_tll);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case OVS_KEY_ATTR_DP_HASH:
|
case OVS_KEY_ATTR_DP_HASH:
|
||||||
md->dp_hash = nl_attr_get_u32(a);
|
md->dp_hash = nl_attr_get_u32(a);
|
||||||
dpif_packet_set_dp_hash(packet, md->dp_hash);
|
dpif_packet_set_dp_hash(packet, md->dp_hash);
|
||||||
@ -275,7 +327,6 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
|
|||||||
case OVS_KEY_ATTR_VLAN:
|
case OVS_KEY_ATTR_VLAN:
|
||||||
case OVS_KEY_ATTR_ICMP:
|
case OVS_KEY_ATTR_ICMP:
|
||||||
case OVS_KEY_ATTR_ICMPV6:
|
case OVS_KEY_ATTR_ICMPV6:
|
||||||
case OVS_KEY_ATTR_ND:
|
|
||||||
case OVS_KEY_ATTR_TCP_FLAGS:
|
case OVS_KEY_ATTR_TCP_FLAGS:
|
||||||
case __OVS_KEY_ATTR_MAX:
|
case __OVS_KEY_ATTR_MAX:
|
||||||
default:
|
default:
|
||||||
@ -348,6 +399,11 @@ odp_execute_masked_set_action(struct dpif_packet *packet,
|
|||||||
get_mask(a, struct ovs_key_arp));
|
get_mask(a, struct ovs_key_arp));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OVS_KEY_ATTR_ND:
|
||||||
|
odp_set_nd(&packet->ofpbuf, nl_attr_get(a),
|
||||||
|
get_mask(a, struct ovs_key_nd));
|
||||||
|
break;
|
||||||
|
|
||||||
case OVS_KEY_ATTR_DP_HASH:
|
case OVS_KEY_ATTR_DP_HASH:
|
||||||
md->dp_hash = nl_attr_get_u32(a)
|
md->dp_hash = nl_attr_get_u32(a)
|
||||||
| (dpif_packet_get_dp_hash(packet) & ~*get_mask(a, uint32_t));
|
| (dpif_packet_get_dp_hash(packet) & ~*get_mask(a, uint32_t));
|
||||||
@ -367,7 +423,6 @@ odp_execute_masked_set_action(struct dpif_packet *packet,
|
|||||||
case OVS_KEY_ATTR_VLAN:
|
case OVS_KEY_ATTR_VLAN:
|
||||||
case OVS_KEY_ATTR_ICMP:
|
case OVS_KEY_ATTR_ICMP:
|
||||||
case OVS_KEY_ATTR_ICMPV6:
|
case OVS_KEY_ATTR_ICMPV6:
|
||||||
case OVS_KEY_ATTR_ND:
|
|
||||||
case OVS_KEY_ATTR_TCP_FLAGS:
|
case OVS_KEY_ATTR_TCP_FLAGS:
|
||||||
case __OVS_KEY_ATTR_MAX:
|
case __OVS_KEY_ATTR_MAX:
|
||||||
default:
|
default:
|
||||||
|
@ -2844,6 +2844,8 @@ static void put_ipv6_key(const struct ovs_key_ipv6 *, struct flow *,
|
|||||||
bool is_mask);
|
bool is_mask);
|
||||||
static void get_arp_key(const struct flow *, struct ovs_key_arp *);
|
static void get_arp_key(const struct flow *, struct ovs_key_arp *);
|
||||||
static void put_arp_key(const struct ovs_key_arp *, struct flow *);
|
static void put_arp_key(const struct ovs_key_arp *, struct flow *);
|
||||||
|
static void get_nd_key(const struct flow *, struct ovs_key_nd *);
|
||||||
|
static void put_nd_key(const struct ovs_key_nd *, struct flow *);
|
||||||
|
|
||||||
/* These share the same layout. */
|
/* These share the same layout. */
|
||||||
union ovs_key_tp {
|
union ovs_key_tp {
|
||||||
@ -4187,6 +4189,45 @@ commit_set_arp_action(const struct flow *flow, struct flow *base_flow,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_nd_key(const struct flow *flow, struct ovs_key_nd *nd)
|
||||||
|
{
|
||||||
|
memcpy(nd->nd_target, &flow->nd_target, sizeof flow->nd_target);
|
||||||
|
/* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
|
||||||
|
memcpy(nd->nd_sll, flow->arp_sha, ETH_ADDR_LEN);
|
||||||
|
memcpy(nd->nd_tll, flow->arp_tha, ETH_ADDR_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
|
||||||
|
{
|
||||||
|
memcpy(&flow->nd_target, &flow->nd_target, sizeof flow->nd_target);
|
||||||
|
/* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
|
||||||
|
memcpy(flow->arp_sha, nd->nd_sll, ETH_ADDR_LEN);
|
||||||
|
memcpy(flow->arp_tha, nd->nd_tll, ETH_ADDR_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum slow_path_reason
|
||||||
|
commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
|
||||||
|
struct ofpbuf *odp_actions,
|
||||||
|
struct flow_wildcards *wc, bool use_masked)
|
||||||
|
{
|
||||||
|
struct ovs_key_nd key, mask, base;
|
||||||
|
|
||||||
|
get_nd_key(flow, &key);
|
||||||
|
get_nd_key(base_flow, &base);
|
||||||
|
get_nd_key(&wc->masks, &mask);
|
||||||
|
|
||||||
|
if (commit(OVS_KEY_ATTR_ND, use_masked, &key, &base, &mask, sizeof key,
|
||||||
|
odp_actions)) {
|
||||||
|
put_nd_key(&base, base_flow);
|
||||||
|
put_nd_key(&mask, &wc->masks);
|
||||||
|
return SLOW_ACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static enum slow_path_reason
|
static enum slow_path_reason
|
||||||
commit_set_nw_action(const struct flow *flow, struct flow *base,
|
commit_set_nw_action(const struct flow *flow, struct flow *base,
|
||||||
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
||||||
@ -4204,7 +4245,7 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
|
|||||||
|
|
||||||
case ETH_TYPE_IPV6:
|
case ETH_TYPE_IPV6:
|
||||||
commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
|
commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
|
||||||
break;
|
return commit_set_nd_action(flow, base, odp_actions, wc, use_masked);
|
||||||
|
|
||||||
case ETH_TYPE_ARP:
|
case ETH_TYPE_ARP:
|
||||||
return commit_set_arp_action(flow, base, odp_actions, wc);
|
return commit_set_arp_action(flow, base, odp_actions, wc);
|
||||||
|
@ -82,6 +82,14 @@ ofp_packet_to_string(const void *data, size_t len)
|
|||||||
struct sctp_header *sh = ofpbuf_l4(&buf);
|
struct sctp_header *sh = ofpbuf_l4(&buf);
|
||||||
ds_put_format(&ds, " sctp_csum:%"PRIx32,
|
ds_put_format(&ds, " sctp_csum:%"PRIx32,
|
||||||
ntohl(get_16aligned_be32(&sh->sctp_csum)));
|
ntohl(get_16aligned_be32(&sh->sctp_csum)));
|
||||||
|
} else if (flow.nw_proto == IPPROTO_ICMP && l4_size >= ICMP_HEADER_LEN) {
|
||||||
|
struct icmp_header *icmph = ofpbuf_l4(&buf);
|
||||||
|
ds_put_format(&ds, " icmp_csum:%"PRIx16,
|
||||||
|
ntohs(icmph->icmp_csum));
|
||||||
|
} else if (flow.nw_proto == IPPROTO_ICMPV6 && l4_size >= ICMP6_HEADER_LEN) {
|
||||||
|
struct icmp6_header *icmp6h = ofpbuf_l4(&buf);
|
||||||
|
ds_put_format(&ds, " icmp6_csum:%"PRIx16,
|
||||||
|
ntohs(icmp6h->icmp6_cksum));
|
||||||
}
|
}
|
||||||
|
|
||||||
ds_put_char(&ds, '\n');
|
ds_put_char(&ds, '\n');
|
||||||
|
@ -104,6 +104,7 @@ static inline const void *ofpbuf_get_tcp_payload(const struct ofpbuf *);
|
|||||||
static inline const void *ofpbuf_get_udp_payload(const struct ofpbuf *);
|
static inline const void *ofpbuf_get_udp_payload(const struct ofpbuf *);
|
||||||
static inline const void *ofpbuf_get_sctp_payload(const struct ofpbuf *);
|
static inline const void *ofpbuf_get_sctp_payload(const struct ofpbuf *);
|
||||||
static inline const void *ofpbuf_get_icmp_payload(const struct ofpbuf *);
|
static inline const void *ofpbuf_get_icmp_payload(const struct ofpbuf *);
|
||||||
|
static inline const void *ofpbuf_get_nd_payload(const struct ofpbuf *);
|
||||||
|
|
||||||
void ofpbuf_use(struct ofpbuf *, void *, size_t);
|
void ofpbuf_use(struct ofpbuf *, void *, size_t);
|
||||||
void ofpbuf_use_stack(struct ofpbuf *, void *, size_t);
|
void ofpbuf_use_stack(struct ofpbuf *, void *, size_t);
|
||||||
@ -374,6 +375,12 @@ static inline const void *ofpbuf_get_icmp_payload(const struct ofpbuf *b)
|
|||||||
? (const char *)ofpbuf_l4(b) + ICMP_HEADER_LEN : NULL;
|
? (const char *)ofpbuf_l4(b) + ICMP_HEADER_LEN : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const void *ofpbuf_get_nd_payload(const struct ofpbuf *b)
|
||||||
|
{
|
||||||
|
return OVS_LIKELY(ofpbuf_l4_size(b) >= ND_MSG_LEN)
|
||||||
|
? (const char *)ofpbuf_l4(b) + ND_MSG_LEN : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DPDK_NETDEV
|
#ifdef DPDK_NETDEV
|
||||||
BUILD_ASSERT_DECL(offsetof(struct ofpbuf, mbuf) == 0);
|
BUILD_ASSERT_DECL(offsetof(struct ofpbuf, mbuf) == 0);
|
||||||
|
|
||||||
|
@ -875,6 +875,58 @@ packet_set_sctp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
|
|||||||
put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum);
|
put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
packet_set_nd(struct ofpbuf *packet, const ovs_be32 target[4],
|
||||||
|
const uint8_t sll[ETH_ADDR_LEN],
|
||||||
|
const uint8_t tll[ETH_ADDR_LEN]) {
|
||||||
|
struct ovs_nd_msg *ns;
|
||||||
|
struct ovs_nd_opt *nd_opt;
|
||||||
|
int bytes_remain = ofpbuf_l4_size(packet);
|
||||||
|
|
||||||
|
if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ns = ofpbuf_l4(packet);
|
||||||
|
nd_opt = &ns->options[0];
|
||||||
|
bytes_remain -= sizeof(*ns);
|
||||||
|
|
||||||
|
if (memcmp(&ns->target, target, sizeof(ovs_be32[4]))) {
|
||||||
|
packet_set_ipv6_addr(packet, IPPROTO_ICMPV6,
|
||||||
|
ns->target.be32,
|
||||||
|
target, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) {
|
||||||
|
if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
|
||||||
|
&& nd_opt->nd_opt_len == 1) {
|
||||||
|
if (memcmp(nd_opt->nd_opt_data, sll, ETH_ADDR_LEN)) {
|
||||||
|
ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
|
||||||
|
|
||||||
|
*csum = recalc_csum48(*csum, nd_opt->nd_opt_data, sll);
|
||||||
|
memcpy(nd_opt->nd_opt_data, sll, ETH_ADDR_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A packet can only contain one SLL or TLL option */
|
||||||
|
break;
|
||||||
|
} else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
|
||||||
|
&& nd_opt->nd_opt_len == 1) {
|
||||||
|
if (memcmp(nd_opt->nd_opt_data, tll, ETH_ADDR_LEN)) {
|
||||||
|
ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
|
||||||
|
|
||||||
|
*csum = recalc_csum48(*csum, nd_opt->nd_opt_data, tll);
|
||||||
|
memcpy(nd_opt->nd_opt_data, tll, ETH_ADDR_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A packet can only contain one SLL or TLL option */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nd_opt += nd_opt->nd_opt_len;
|
||||||
|
bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
packet_tcp_flag_to_string(uint32_t flag)
|
packet_tcp_flag_to_string(uint32_t flag)
|
||||||
{
|
{
|
||||||
|
@ -647,6 +647,27 @@ struct icmp6_header {
|
|||||||
};
|
};
|
||||||
BUILD_ASSERT_DECL(ICMP6_HEADER_LEN == sizeof(struct icmp6_header));
|
BUILD_ASSERT_DECL(ICMP6_HEADER_LEN == sizeof(struct icmp6_header));
|
||||||
|
|
||||||
|
/* Neighbor Discovery option field.
|
||||||
|
* ND options are always a multiple of 8 bytes in size. */
|
||||||
|
#define ND_OPT_LEN 8
|
||||||
|
struct ovs_nd_opt {
|
||||||
|
uint8_t nd_opt_type; /* Values defined in icmp6.h */
|
||||||
|
uint8_t nd_opt_len; /* in units of 8 octets (the size of this struct) */
|
||||||
|
uint8_t nd_opt_data[6]; /* Ethernet address in the case of SLL or TLL options */
|
||||||
|
};
|
||||||
|
BUILD_ASSERT_DECL(ND_OPT_LEN == sizeof(struct ovs_nd_opt));
|
||||||
|
|
||||||
|
/* Like struct nd_msg (from ndisc.h), but whereas that struct requires 32-bit
|
||||||
|
* alignment, this one only requires 16-bit alignment. */
|
||||||
|
#define ND_MSG_LEN 24
|
||||||
|
struct ovs_nd_msg {
|
||||||
|
struct icmp6_header icmph;
|
||||||
|
ovs_16aligned_be32 rco_flags;
|
||||||
|
union ovs_16aligned_in6_addr target;
|
||||||
|
struct ovs_nd_opt options[0];
|
||||||
|
};
|
||||||
|
BUILD_ASSERT_DECL(ND_MSG_LEN == sizeof(struct ovs_nd_msg));
|
||||||
|
|
||||||
/* The IPv6 flow label is in the lower 20 bits of the first 32-bit word. */
|
/* The IPv6 flow label is in the lower 20 bits of the first 32-bit word. */
|
||||||
#define IPV6_LABEL_MASK 0x000fffff
|
#define IPV6_LABEL_MASK 0x000fffff
|
||||||
|
|
||||||
@ -758,6 +779,8 @@ void packet_set_ipv6(struct ofpbuf *, uint8_t proto, const ovs_be32 src[4],
|
|||||||
void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
|
void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
|
||||||
void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
|
void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
|
||||||
void packet_set_sctp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
|
void packet_set_sctp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
|
||||||
|
void packet_set_nd(struct ofpbuf *, const ovs_be32 target[4],
|
||||||
|
const uint8_t sll[6], const uint8_t tll[6]);
|
||||||
|
|
||||||
void packet_format_tcp_flags(struct ds *, uint16_t);
|
void packet_format_tcp_flags(struct ds *, uint16_t);
|
||||||
const char *packet_tcp_flag_to_string(uint32_t flag);
|
const char *packet_tcp_flag_to_string(uint32_t flag);
|
||||||
|
@ -934,6 +934,23 @@ OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,dp_hash=0x1234567/0xfff
|
|||||||
])
|
])
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field nd_target, nd_sll])
|
||||||
|
AT_KEYWORDS([ofp-print])
|
||||||
|
AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
|
||||||
|
03 0e 00 78 00 00 00 02 00 00 00 00 00 00 00 00 \
|
||||||
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \
|
||||||
|
ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
|
||||||
|
00 01 00 14 80 00 0a 02 86 dd 80 00 14 01 3a 80 \
|
||||||
|
00 3a 01 87 00 00 00 00 00 04 00 30 00 00 00 00 \
|
||||||
|
00 19 00 18 80 00 3e 10 00 00 00 00 00 00 00 00 \
|
||||||
|
00 00 00 00 00 00 00 01 00 19 00 10 80 00 40 06 \
|
||||||
|
aa aa aa aa aa aa 00 00
|
||||||
|
" 2], [0], [dnl
|
||||||
|
OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD priority=255,icmp6,icmp_type=135 actions=set_field:::1->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll
|
||||||
|
], [dnl
|
||||||
|
])
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
dnl This triggered a buggy "instructions out of order" message earlier.
|
dnl This triggered a buggy "instructions out of order" message earlier.
|
||||||
AT_SETUP([OFPT_FLOW_MOD - OF1.3 - meter])
|
AT_SETUP([OFPT_FLOW_MOD - OF1.3 - meter])
|
||||||
AT_KEYWORDS([ofp-print])
|
AT_KEYWORDS([ofp-print])
|
||||||
|
@ -259,6 +259,25 @@ Datapath actions: 10,set(ipv4(src=192.168.3.91)),11,set(ipv4(src=192.168.3.90)),
|
|||||||
OVS_VSWITCHD_STOP
|
OVS_VSWITCHD_STOP
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([ofproto-dpif - modify IPv6 Neighbor Solitication (ND)])
|
||||||
|
OVS_VSWITCHD_START
|
||||||
|
ADD_OF_PORTS([br0], [1], [10], [11], [12], [13])
|
||||||
|
AT_DATA([flows.txt], [dnl
|
||||||
|
table=0 in_port=1,icmp6,icmpv6_type=135 actions=output(10),write_actions(set_field:fe80::3->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll,output(12)),goto_table(1)
|
||||||
|
table=1 icmp6 actions=write_actions(output(13)),goto_table(2)
|
||||||
|
table=2 in_port=1,icmp6,icmpv6_type=135 actions=set_field:fe80::4->nd_target,set_field:cc:cc:cc:cc:cc:cc->nd_sll,output(11)
|
||||||
|
])
|
||||||
|
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
|
||||||
|
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,icmp6,ipv6_src=fe80::1,ipv6_dst=fe80::2,nw_tos=0,nw_ttl=128,icmpv6_type=135,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11'], [0], [stdout])
|
||||||
|
AT_CHECK([tail -4 stdout], [0],
|
||||||
|
[Megaflow: recirc_id=0,icmp6,in_port=1,nw_frag=no,icmp_type=135,icmp_code=0x0/0xff,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11
|
||||||
|
Datapath actions: 10,set(nd(target=fe80::4,sll=cc:cc:cc:cc:cc:cc)),11,set(nd(target=fe80::3,sll=aa:aa:aa:aa:aa:aa)),13
|
||||||
|
This flow is handled by the userspace slow path because it:
|
||||||
|
- Uses action(s) not supported by datapath.
|
||||||
|
])
|
||||||
|
OVS_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
AT_SETUP([ofproto-dpif - clear actions])
|
AT_SETUP([ofproto-dpif - clear actions])
|
||||||
OVS_VSWITCHD_START
|
OVS_VSWITCHD_START
|
||||||
ADD_OF_PORTS([br0], [1], [10], [11], [12])
|
ADD_OF_PORTS([br0], [1], [10], [11], [12])
|
||||||
@ -6366,8 +6385,30 @@ OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
|
|||||||
|
|
||||||
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
|
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
|
||||||
NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via no_match) data_len=86 (unbuffered)
|
NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via no_match) data_len=86 (unbuffered)
|
||||||
icmp6,in_port=0,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00
|
icmp6,in_port=0,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00 icmp6_csum:68bd
|
||||||
])
|
])
|
||||||
|
|
||||||
OVS_VSWITCHD_STOP
|
OVS_VSWITCHD_STOP
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([ofproto-dpif - Neighbor Discovery set-field with checksum update])
|
||||||
|
OVS_VSWITCHD_START
|
||||||
|
ADD_OF_PORTS([br0], 1)
|
||||||
|
AT_CHECK([ovs-ofctl add-flow br0 icmp6,icmpv6_type=135,action=set_field:fe80::1-\>nd_target,set_field:32:21:14:86:11:74-\>nd_sll,output:controller])
|
||||||
|
|
||||||
|
AT_CAPTURE_FILE([ofctl_monitor.log])
|
||||||
|
|
||||||
|
AT_CHECK([ovs-ofctl monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
|
||||||
|
|
||||||
|
ovs-appctl netdev-dummy/receive p1 '0060970769ea0000860580da86dd6000000000203afffe80000000000000020086fffe0580dafe80000000000000026097fffe0769ea870068bd00000000fe80000000000000026097fffe0769ea01010000860580da'
|
||||||
|
|
||||||
|
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
|
||||||
|
|
||||||
|
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
|
||||||
|
NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via action) data_len=86 (unbuffered)
|
||||||
|
icmp6,in_port=0,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::1,nd_sll=32:21:14:86:11:74,nd_tll=00:00:00:00:00:00 icmp6_csum:19d3
|
||||||
|
])
|
||||||
|
|
||||||
|
OVS_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
|
@ -844,7 +844,9 @@ AT_SETUP([ofproto - set-field flow_mod commands (NXM)])
|
|||||||
OVS_VSWITCHD_START
|
OVS_VSWITCHD_START
|
||||||
AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=drop])
|
AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=drop])
|
||||||
AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src])
|
AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src])
|
||||||
|
AT_CHECK([ovs-ofctl add-flow br0 icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll])
|
||||||
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
|
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
|
||||||
|
table=1, icmp6,in_port=3,icmp_type=136 actions=load:0xa6badbfff00d59fa->NXM_NX_ND_TARGET[[0..63]],load:0xfe8086753097890a->NXM_NX_ND_TARGET[[64..127]],load:0xccddeeff0011->NXM_NX_ND_TLL[[]]
|
||||||
table=1, ipv6,in_port=3 actions=load:0xa6badbfffefe59fa->NXM_NX_IPV6_SRC[[0..63]],load:0xfe8001234567890a->NXM_NX_IPV6_SRC[[64..127]]
|
table=1, ipv6,in_port=3 actions=load:0xa6badbfffefe59fa->NXM_NX_IPV6_SRC[[0..63]],load:0xfe8001234567890a->NXM_NX_IPV6_SRC[[64..127]]
|
||||||
NXST_FLOW reply:
|
NXST_FLOW reply:
|
||||||
])
|
])
|
||||||
@ -870,6 +872,19 @@ AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip], [0], [OFPST_FLO
|
|||||||
OVS_VSWITCHD_STOP
|
OVS_VSWITCHD_STOP
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([ofproto - set-field flow_mod commands (OF1.2)])
|
||||||
|
OVS_VSWITCHD_START
|
||||||
|
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 ipv6,table=1,in_port=3,actions=drop])
|
||||||
|
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src])
|
||||||
|
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll])
|
||||||
|
AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0], [dnl
|
||||||
|
table=1, icmp6,in_port=3,icmp_type=136 actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa->nd_target,set_field:cc:dd:ee:ff:00:11->nd_tll
|
||||||
|
table=1, ipv6,in_port=3 actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
|
||||||
|
OFPST_FLOW reply (OF1.2):
|
||||||
|
])
|
||||||
|
OVS_VSWITCHD_STOP
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
AT_SETUP([ofproto - dump flows with cookie])
|
AT_SETUP([ofproto - dump flows with cookie])
|
||||||
OVS_VSWITCHD_START
|
OVS_VSWITCHD_START
|
||||||
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
|
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
|
||||||
@ -1380,7 +1395,7 @@ OVS_VSWITCHD_START
|
|||||||
instructions: apply_actions,clear_actions,write_actions,write_metadata$goto
|
instructions: apply_actions,clear_actions,write_actions,write_metadata$goto
|
||||||
Write-Actions and Apply-Actions features:
|
Write-Actions and Apply-Actions features:
|
||||||
actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
|
actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
|
||||||
supported on Set-Field: metadata in_port_oxm eth_src eth_dst vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label ip_dscp nw_ecn arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst
|
supported on Set-Field: metadata in_port_oxm eth_src eth_dst vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label ip_dscp nw_ecn arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll
|
||||||
matching:
|
matching:
|
||||||
metadata: exact match or wildcard
|
metadata: exact match or wildcard
|
||||||
in_port_oxm: exact match or wildcard
|
in_port_oxm: exact match or wildcard
|
||||||
@ -1465,7 +1480,7 @@ OVS_VSWITCHD_START
|
|||||||
instructions: meter,apply_actions,clear_actions,write_actions,write_metadata$goto
|
instructions: meter,apply_actions,clear_actions,write_actions,write_metadata$goto
|
||||||
Write-Actions and Apply-Actions features:
|
Write-Actions and Apply-Actions features:
|
||||||
actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
|
actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
|
||||||
supported on Set-Field: tun_id tun_src tun_dst metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst
|
supported on Set-Field: tun_id tun_src tun_dst metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll
|
||||||
matching:
|
matching:
|
||||||
dp_hash: arbitrary mask
|
dp_hash: arbitrary mask
|
||||||
recirc_id: exact match or wildcard
|
recirc_id: exact match or wildcard
|
||||||
|
Loading…
x
Reference in New Issue
Block a user