-
Notifications
You must be signed in to change notification settings - Fork 647
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for port mirroring CLIs #936
Changes from 2 commits
f66a8cc
c2dab24
47f4a93
cd23175
9588c94
d492005
8696423
8b332bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -570,6 +570,82 @@ def is_ipaddress(val): | |
return False | ||
return True | ||
|
||
# | ||
# Check if an interface_name is in a vlan | ||
# | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def interface_is_in_vlan(vlan_member_table, interface_name): | ||
|
||
for k,v in vlan_member_table: | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if v == interface_name: | ||
return True | ||
|
||
return False | ||
|
||
|
||
# | ||
# Check if port is already configured as mirror destination port | ||
# | ||
def interface_is_mirror_dst_port(config_db, interface_name): | ||
mirror_table = config_db.get_table('MIRROR_SESSION') | ||
for k,v in mirror_table.items(): | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if 'dst_port' in v and v['dst_port'] == interface_name: | ||
return True | ||
|
||
return False | ||
# | ||
# Check if port is already configured with mirror config | ||
# | ||
def interface_has_mirror_config(mirror_table, interface_name): | ||
for k,v in mirror_table.items(): | ||
if 'src_port' in v and v['src_port'] == interface_name: | ||
return True | ||
if 'dst_port' in v and v['dst_port'] == interface_name: | ||
return True | ||
|
||
return False | ||
|
||
# | ||
# Check if SPAN mirror-session config is valid. | ||
# | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def validate_mirror_session_config(config_db, session_name, dst_port, src_port, direction): | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(config_db.get_entry('MIRROR_SESSION', session_name)) != 0: | ||
click.echo("Error: {} already exists".format(session_name)) | ||
return False | ||
|
||
vlan_member_table = config_db.get_table('VLAN_MEMBER') | ||
mirror_table = config_db.get_table('MIRROR_SESSION') | ||
|
||
if dst_port is not None: | ||
if interface_name_is_valid(dst_port) is False: | ||
click.echo("Error: Destination Interface {} is invalid".format(dst_port)) | ||
return False | ||
|
||
if interface_is_in_vlan(vlan_member_table, dst_port): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the port is a routed port, SPAN session is supported? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. Not supported. Please check in latest patchset. |
||
click.echo("Error: Destination Interface {} has vlan config".format(dst_port)) | ||
return False | ||
|
||
if interface_has_mirror_config(mirror_table, dst_port): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if I understand this correctly, can a single dst port cannot be a monitor (mirror-to) port There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. This is not supported. We allow only one mirror session per port, The session can have any number of ports. |
||
click.echo("Error: Destination Interface {} already has mirror config".format(dst_port)) | ||
return False | ||
|
||
if src_port is not None: | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for port in src_port.split(","): | ||
if interface_name_is_valid(port) is False: | ||
click.echo("Error: Source Interface {} is invalid".format(port)) | ||
return False | ||
if dst_port is not None and dst_port == port: | ||
click.echo("Error: Destination Interface cant be same as Source Interface") | ||
return False | ||
if interface_has_mirror_config(mirror_table, port): | ||
click.echo("Error: Source Interface {} already has mirror config".format(port)) | ||
return False | ||
|
||
if direction is not None: | ||
if not any ( [direction == 'rx', direction == 'tx', direction == 'both'] ): | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
click.echo("Error: Direction {} is invalid".format(direction)) | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return False | ||
|
||
return True | ||
|
||
# This is our main entrypoint - the main 'config' command | ||
@click.group(cls=AbbreviationGroup, context_settings=CONTEXT_SETTINGS) | ||
|
@@ -1017,20 +1093,37 @@ def del_portchannel_member(ctx, portchannel_name, port_name): | |
def mirror_session(): | ||
pass | ||
|
||
@mirror_session.command() | ||
# | ||
# 'add' subgroup ('config mirror_session add ...') | ||
# | ||
|
||
@mirror_session.group(cls=AbbreviationGroup, name='add') | ||
@click.pass_context | ||
def add(ctx): | ||
"""Add mirror_session""" | ||
pass | ||
|
||
# | ||
# 'add' subcommand | ||
# | ||
|
||
@add.command('erspan') | ||
@click.argument('session_name', metavar='<session_name>', required=True) | ||
@click.argument('src_ip', metavar='<src_ip>', required=True) | ||
@click.argument('dst_ip', metavar='<dst_ip>', required=True) | ||
@click.argument('dscp', metavar='<dscp>', required=True) | ||
@click.argument('ttl', metavar='<ttl>', required=True) | ||
@click.argument('gre_type', metavar='[gre_type]', required=False) | ||
@click.argument('queue', metavar='[queue]', required=False) | ||
@click.argument('src_port', metavar='[src_port]', required=False) | ||
@click.argument('direction', metavar='[direction]', required=False) | ||
@click.option('--policer') | ||
def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): | ||
def erspan(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer, src_port, direction): | ||
""" | ||
Add mirror session | ||
Add ERSPAN mirror session | ||
""" | ||
session_info = { | ||
"type" : "ERSPAN", | ||
"src_ip": src_ip, | ||
"dst_ip": dst_ip, | ||
"dscp": dscp, | ||
|
@@ -1045,20 +1138,94 @@ def add(session_name, src_ip, dst_ip, dscp, ttl, gre_type, queue, policer): | |
|
||
if queue is not None: | ||
session_info['queue'] = queue | ||
|
||
|
||
if src_port is not None: | ||
if get_interface_naming_mode() == "alias": | ||
src_port_list = [] | ||
for port in src_port.split(","): | ||
src_port_list.append(interface_alias_to_name(port)) | ||
src_port=",".join(src_port_list) | ||
|
||
session_info['src_port'] = src_port | ||
|
||
if direction is not None: | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
session_info['direction'] = direction.upper() | ||
|
||
""" | ||
For multi-npu platforms we need to program all front asic namespaces | ||
""" | ||
namespaces = sonic_device_util.get_all_namespaces() | ||
if not namespaces['front_ns']: | ||
config_db = ConfigDBConnector() | ||
config_db.connect() | ||
if validate_mirror_session_config(config_db, session_name, None, src_port, direction) is False: | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return | ||
config_db.set_entry("MIRROR_SESSION", session_name, session_info) | ||
else: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @abdosi could you take a quick look at the multi-NPU logic here and make sure it looks OK to you? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validation need to be updated so that src_port and dst_port are in same asic/namespace. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. will take of this in next PR. How do I validate this ? |
||
per_npu_configdb = {} | ||
for front_asic_namespaces in namespaces['front_ns']: | ||
per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) | ||
per_npu_configdb[front_asic_namespaces].connect() | ||
if validate_mirror_session_config(per_npu_configdb[front_asic_namespaces], session_name, None, src_port, direction) is False: | ||
return | ||
per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) | ||
|
||
@add.command('span') | ||
@click.argument('session_name', metavar='<session_name>', required=True) | ||
@click.argument('dst_port', metavar='<dst_port>', required=True) | ||
@click.argument('src_port', metavar='[src_port]', required=False) | ||
@click.argument('direction', metavar='[direction]', required=False) | ||
@click.argument('queue', metavar='[queue]', required=False) | ||
@click.option('--policer') | ||
def span(session_name, dst_port, src_port, direction, queue, policer): | ||
""" | ||
Add port mirror session | ||
""" | ||
if get_interface_naming_mode() == "alias": | ||
dst_port = interface_alias_to_name(dst_port) | ||
if dst_port is None: | ||
click.echo("Error: Destination Interface {} is invalid".format(dst_port)) | ||
return | ||
if src_port is not None: | ||
src_port_list = [] | ||
for port in src_port.split(","): | ||
src_port_list.append(interface_alias_to_name(port)) | ||
src_port=",".join(src_port_list) | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
session_info = { | ||
"type" : "SPAN", | ||
"dst_port": dst_port, | ||
} | ||
|
||
if src_port is not None: | ||
session_info['src_port'] = src_port | ||
|
||
if direction is not None: | ||
session_info['direction'] = direction.upper() | ||
|
||
if policer is not None: | ||
session_info['policer'] = policer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the need for policer and queue for SPAN sessions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is existing support in community. This is to police the mirrored traffic. |
||
|
||
if queue is not None: | ||
session_info['queue'] = queue | ||
|
||
""" | ||
For multi-npu platforms we need to program all front asic namespaces | ||
""" | ||
namespaces = sonic_device_util.get_all_namespaces() | ||
abdosi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not namespaces['front_ns']: | ||
config_db = ConfigDBConnector() | ||
config_db.connect() | ||
if validate_mirror_session_config(config_db, session_name, dst_port, src_port, direction) is False: | ||
return | ||
config_db.set_entry("MIRROR_SESSION", session_name, session_info) | ||
else: | ||
per_npu_configdb = {} | ||
for front_asic_namespaces in namespaces['front_ns']: | ||
per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces) | ||
per_npu_configdb[front_asic_namespaces].connect() | ||
if validate_mirror_session_config(per_npu_configdb[front_asic_namespaces], session_name, dst_port, src_port, direction) is False: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validation need to be updated so that src_port and dst_port are in same asic/namespace. |
||
return | ||
per_npu_configdb[front_asic_namespaces].set_entry("MIRROR_SESSION", session_name, session_info) | ||
|
||
@mirror_session.command() | ||
|
@@ -1356,6 +1523,9 @@ def add_vlan_member(ctx, vid, interface_name, untagged): | |
|
||
if len(vlan) == 0: | ||
ctx.fail("{} doesn't exist".format(vlan_name)) | ||
if interface_is_mirror_dst_port(db, interface_name): | ||
ctx.fail("{} is configured as mirror destination port".format(interface_name)) | ||
|
||
members = vlan.get('members', []) | ||
if interface_name in members: | ||
if get_interface_naming_mode() == "alias": | ||
|
@@ -1370,7 +1540,7 @@ def add_vlan_member(ctx, vid, interface_name, untagged): | |
for entry in interface_table: | ||
if (interface_name == entry[0]): | ||
ctx.fail("{} is a L3 interface!".format(interface_name)) | ||
|
||
members.append(interface_name) | ||
vlan['members'] = members | ||
db.set_entry('VLAN', vlan_name, vlan) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3747,7 +3747,6 @@ This command deletes the SNMP Trap server IP address to which SNMP agent is expe | |
|
||
Go Back To [Beginning of the document](#) or [Beginning of this section](#management-vrf) | ||
|
||
|
||
## Mirroring | ||
|
||
### Mirroring Show commands | ||
|
@@ -3763,39 +3762,82 @@ This command displays all the mirror sessions that are configured. | |
|
||
- Example: | ||
``` | ||
admin@sonic:~$ show mirror session | ||
Name Status SRC IP DST IP GRE DSCP TTL Queue | ||
--------- -------- --------- -------- ----- ------ ----- ------- | ||
admin@sonic:~$ show mirror_session | ||
ERSPAN Sessions | ||
Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction | ||
------ -------- -------- -------- ----- ------ ----- ------- --------- -------------- ---------- ----------- | ||
everflow0 active 10.1.0.32 10.0.0.7 | ||
|
||
SPAN Sessions | ||
Name Status DST Port SRC Port Direction | ||
------ -------- ---------- ------------- ----------- | ||
port0 active Ethernet0 PortChannel10 rx | ||
``` | ||
|
||
### Mirroring Config commands | ||
|
||
**config mirror_session** | ||
|
||
This command is used to add or remove mirroring sessions. Mirror session is identified by "session_name". | ||
While adding a new session, users need to configure the following fields that are used while forwarding the mirrored packets. | ||
This command supports configuring both SPAN/ERSPAN sessions. | ||
In SPAN user can configure mirroring of list of source ports/LAG to destination port in ingress/egress/both directions. | ||
In ERSPAN user can configure mirroring of list of source ports/LAG to a destination IP. | ||
Both SPAN/ERSPAN support ACL based mirroring and can be used in ACL configurations. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we have Everflow ACL table with everflow mirror session and Data ACL Table with port based mirror session ? |
||
|
||
While adding a new ERSPAN session, users need to configure the following fields that are used while forwarding the mirrored packets. | ||
|
||
1) source IP address, | ||
2) destination IP address, | ||
3) DSCP (QoS) value with which mirrored packets are forwarded | ||
4) TTL value | ||
5) optional - GRE Type in case if user wants to send the packet via GRE tunnel. GRE type could be anything; it could also be left as empty; by default, it is 0x8949 for Mellanox; and 0x88be for the rest of the chips. | ||
6) optional - Queue in which packets shall be sent out of the device. Valid values 0 to 7 for most of the devices. Users need to know their device and the number of queues supported in that device. | ||
7) optional - Policer which will be used to control the rate at which frames are mirrored. | ||
8) optional - List of source ports which can have both Ethernet and LAG ports. | ||
9) optional - Direction - Mirror session direction when configured along with Source port. | ||
|
||
- Usage: | ||
``` | ||
config mirror_session add <session_name> <src_ip> <dst_ip> <dscp> <ttl> [gre_type] [queue] | ||
config mirror_session add erspan <session_name> <src_ip> <dst_ip> <dscp> <ttl> [gre_type] [queue] [policer <policer_name>] [source-port-list] [direction] | ||
daall marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
- Example: | ||
``` | ||
admin@sonic:~$ sudo config mirror_session add mrr_abcd 1.2.3.4 20.21.22.23 8 100 0x6558 0 | ||
admin@sonic:~$ show mirror_session | ||
Name Status SRC IP DST IP GRE DSCP TTL Queue | ||
--------- -------- ----------- ----------- ------ ------ ----- ------- | ||
mrr_abcd inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 | ||
root@T1-2:~# config mirror_session add erspan mrr_abcd 1.2.3.4 20.21.22.23 8 100 0x6558 0 | ||
root@T1-2:~# show mirror_session | ||
Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction | ||
--------- -------- -------- ----------- ------ ------ ----- ------- --------- -------------- ---------- ----------- | ||
mrr_abcd inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 | ||
root@T1-2:~# | ||
|
||
root@T1-2:~# config mirror_session add erspan mrr_port 1.2.3.4 20.21.22.23 8 100 0x6558 0 Ethernet0 both | ||
root@T1-2:~# show mirror_session | ||
Name Status SRC IP DST IP GRE DSCP TTL Queue Policer Monitor Port SRC Port Direction | ||
--------- -------- -------- ----------- ------ ------ ----- ------- --------- -------------- ---------- ----------- | ||
mrr_port inactive 1.2.3.4 20.21.22.23 0x6558 8 100 0 Ethernet0 both | ||
root@T1-2:~# | ||
``` | ||
|
||
While adding a new SPAN session, users need to configure the following fields that are used while forwarding the mirrored packets. | ||
1) destination port, | ||
2) optional - List of source ports- List of source ports which can have both Ethernet and LAG ports. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a note that atleast one port/LAG is required for local span? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can have local span session with only destination port, which can be used in ACL mirroring. |
||
3) optional - Direction - Mirror session direction when configured along with Source port. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you specify the default direction as this is optional? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When source port is specified, we can make direction default as both. Let me push that change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. taken care of this. Thanks. |
||
4) optional - Queue in which packets shall be sent out of the device. Valid values 0 to 7 for most of the devices. Users need to know their device and the number of queues supported in that device. | ||
5) optional - Policer which will be used to control the rate at which frames are mirrored. | ||
|
||
- Usage: | ||
``` | ||
config mirror_session add span <session_name> <dst_port> [source-port-list] [direction] [queue] [policer <policer_name>] | ||
``` | ||
|
||
- Example: | ||
``` | ||
root@T1-2:~# config mirror_session add span port0 Ethernet0 Ethernet4,PortChannel001,Ethernet8 both | ||
root@T1-2:~# show mirror_session | ||
Name Status DST Port SRC Port Direction | ||
------ -------- ---------- --------------------------------- ----------- | ||
port0 active Ethernet0 Ethernet4,PortChannel10,Ethernet8 both | ||
root@T1-2:~# | ||
|
||
Go Back To [Beginning of the document](#) or [Beginning of this section](#mirroring) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the need for policer and queue for SPAN sessions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above. Extending it to SPAN also.