Skip to content

Commit

Permalink
Merge pull request #3094 from MichaelWasher/kill_on_exception_with_fi…
Browse files Browse the repository at this point in the history
…lter_id

Add dynamic ACLs for chewie
  • Loading branch information
anarkiwi committed Jul 4, 2019
2 parents 20bc25b + 74c4abc commit d4ff92d
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 45 deletions.
12 changes: 10 additions & 2 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,16 @@ with the configuration block 'dot1x':
- Shared secret used by the RADIUS server and the 802.1X speaker. - NOTE: Faucet will only use the config from the first dp
* - noauth_acl
- str
-
- None
- The name of the defined ACL [refer to acls.yaml for more information] that will be set to all 802.1X ports by default, that is before any user is authenticated. - NOTE: Faucet will only use the config from the first dp
* - auth_acl
- str
-
- None
- The name of the defined ACL [refer to acls.yaml for more information] that will be set to an 802.1X port when a user authenticates. - NOTE: Faucet will only use the config from the first dp
* - dot1x_assigned
- boolean
- False
- True, if this ACL can be dynamically assigned by a RADIUS server during 802.1X authentication.



Expand Down Expand Up @@ -450,6 +454,10 @@ OFP port number ranges (eg. 1-6).
- boolean
- False
- Enable 802.1X Mac Authentication Bypass on port (NOTE: Requires dot1x attribute)
* - enabled
- boolean
- True
- Enable 802.1X Per User ACLs on port (NOTE: Requires dot1x attribute and ACL with dot1x_assigned)
* - enabled
- boolean
- True
Expand Down
3 changes: 3 additions & 0 deletions faucet/acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ class ACL(Conf):
defaults = {
'rules': None,
'exact_match': False,
'dot1x_assigned': False,
}
defaults_types = {
'rules': list,
'exact_match': bool,
'dot1x_assigned': bool,
}
rule_types = {
'cookie': int,
Expand Down Expand Up @@ -105,6 +107,7 @@ class ACL(Conf):
def __init__(self, _id, dp_id, conf):
self.rules = []
self.exact_match = None
self.dot1x_assigned = None
self.meter = False
self.matches = {}
self.set_fields = set()
Expand Down
46 changes: 25 additions & 21 deletions faucet/dp.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,15 +383,13 @@ def _generate_acl_tables(self):
all_acls = {}
if self.dot1x:
# NOTE: All acl's are added to the acl list and then referred to later by ports
all_acls['port_acl'] = [PORT_ACL_8021X, MAB_ACL_8021X]
acls = [PORT_ACL_8021X, MAB_ACL_8021X,
self.acls.get(self.dot1x.get('auth_acl'), None),
self.acls.get(self.dot1x.get('noauth_acl'), None)
]

auth_acl = self.acls.get(self.dot1x.get('auth_acl'))
noauth_acl = self.acls.get(self.dot1x.get('noauth_acl'))

if auth_acl:
all_acls['port_acl'].append(auth_acl)
if noauth_acl:
all_acls['port_acl'].append(noauth_acl)
acls.extend([acl for acl_name, acl in self.acls.items() if acl.dot1x_assigned])
all_acls['port_acl'] = [acl for acl in acls if acl is not None]

for vlan in self.vlans.values():
if vlan.acls_in:
Expand Down Expand Up @@ -1065,28 +1063,34 @@ def resolve_acls():
vlan.acls_out = acls
verify_acl_exact_match(acls)
for port in self.ports.values():
acls = []
if port.acls_in:
acls = []
test_config_condition(self.dp_acls, (
'dataplane ACLs cannot be used with port ACLs.'))
for acl in port.acls_in:
resolve_acl(acl, port_num=port.number)
acls.append(self.acls[acl])
resolved.append(acl)
port.acls_in = acls
verify_acl_exact_match(acls)

if port.dot1x_dyn_acl:
acl_names = [acl_name for acl_name, acl in self.acls.items()
if acl.dot1x_assigned]

for acl_name in acl_names:
resolve_acl(acl_name, port_num=port.number)
resolved.append(acl_name)

if port.dot1x_acl:
if self.dot1x.get('noauth_acl'):
noauth_acl = self.dot1x.get('noauth_acl')
resolve_acl(noauth_acl, port_num=port.number)
resolved.append(noauth_acl)

if self.dot1x.get('auth_acl'):
auth_acl = self.dot1x.get('auth_acl')
resolve_acl(auth_acl, port_num=port.number)
resolved.append(auth_acl)

port.acls_in = acls
verify_acl_exact_match(acls)
acl_names = [self.dot1x.get('auth_acl'),
self.dot1x.get('noauth_acl')]

for acl_name in acl_names:
if self.acls.get(acl_name, None):
resolve_acl(acl_name, port_num=port.number)
resolved.append(acl_name)

if self.dp_acls:
acls = []
for acl in self.acls:
Expand Down
31 changes: 22 additions & 9 deletions faucet/faucet_dot1x.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ def _get_valve_and_port(self, port_id):
valve, port = self.mac_to_port[port_id]
return (valve, port)

def _get_acls(self, dp):
def _get_acls(self, datapath):
"""Returns tuple of acl values"""
auth_acl = dp.acls.get(self._auth_acl_name)
noauth_acl = dp.acls.get(self._noauth_acl_name)
auth_acl = datapath.acls.get(self._auth_acl_name)
noauth_acl = datapath.acls.get(self._noauth_acl_name)
return (auth_acl, noauth_acl)

# Loggin Methods
Expand Down Expand Up @@ -118,8 +118,8 @@ def auth_handler(self, address, port_id, *args, **kwargs): # pylint: disable=un

self.log_auth_event(valve, port_num, address_str, 'success')
flowmods = self._get_login_flowmod(dot1x_port, valve, address_str,
kwargs.get('vlan_name', None))

kwargs.get('vlan_name', None),
kwargs.get('filter_id', None))
if flowmods:
self._send_flow_msgs(valve, flowmods)

Expand Down Expand Up @@ -331,23 +331,34 @@ def _get_logoff_flowmod(self, dot1x_port, valve, mac_str):
self._add_unauthenticated_flowmod(dot1x_port, valve))
return flowmods

def _get_login_flowmod(self, dot1x_port, valve, mac_str, vlan_name):
def _get_login_flowmod(self, dot1x_port, valve, # pylint: disable=too-many-arguments
mac_str, vlan_name, acl_name):
"""Return flowmods required to login port"""
flowmods = []
flowmods.extend(
self._del_unauthenticated_flowmod(dot1x_port, valve))
flowmods.extend(
self._add_authenticated_flowmod(dot1x_port, valve, mac_str, vlan_name))
self._add_authenticated_flowmod(dot1x_port, valve, mac_str, vlan_name, acl_name))
return flowmods

def _add_authenticated_flowmod(self, dot1x_port, valve, mac_str, vlan_name):
def _add_authenticated_flowmod(self, dot1x_port, valve, # pylint: disable=too-many-arguments
mac_str, vlan_name, acl_name):
"""Return flowmods for successful authentication on port"""
port_num = dot1x_port.number
flowmods = []
acl_manager = valve.acl_manager

if dot1x_port.dot1x_acl:
acl = valve.dp.acls.get(acl_name, None)
if dot1x_port.dot1x_dyn_acl and acl:
self.logger.info("DOT1X_DYN_ACL: Adding ACL '{0}' for port '{1}'".format(
acl_name, port_num))
self.logger.debug("DOT1X_DYN_ACL: ACL contents: '{0}'".format(str(acl.__dict__)))
flowmods.extend(acl_manager.add_port_acl(acl, port_num, mac_str))
elif dot1x_port.dot1x_acl:
auth_acl, _ = self._get_acls(valve.dp)
self.logger.info("DOT1X_PRE_ACL: Adding ACL '{0}' for port '{1}'".format(
acl_name, port_num))
self.logger.debug("DOT1X_PRE_ACL: ACL contents: '{0}'".format(str(auth_acl.__dict__)))
flowmods.extend(acl_manager.add_port_acl(auth_acl, port_num, mac_str))
else:
flowmods.extend(acl_manager.add_authed_mac(port_num, mac_str))
Expand All @@ -365,6 +376,8 @@ def _del_authenticated_flowmod(self, dot1x_port, valve, mac_str):
if dot1x_port.dot1x_acl:
auth_acl, _ = self._get_acls(valve.dp)
flowmods.extend(acl_manager.del_port_acl(auth_acl, port_num, mac_str))
elif dot1x_port.dot1x_dyn_acl:
flowmods.extend(acl_manager.del_authed_mac(port_num, mac_str, strict=False))
else:
flowmods.extend(acl_manager.del_authed_mac(port_num, mac_str))

Expand Down
14 changes: 13 additions & 1 deletion faucet/port.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class Port(Conf):
# If true, expects authentication and default ACLs for 802.1x auth
'dot1x_mab': False,
# If true, allows Mac Auth Bypass on port (NOTE: this is less secure as MACs can be spoofed)

'dot1x_dyn_acl': False,
# If true, expects authentication and ACLs with dot1x_assigned flag set
}

defaults_types = {
Expand Down Expand Up @@ -121,6 +122,7 @@ class Port(Conf):
'dot1x': bool,
'dot1x_acl': bool,
'dot1x_mab': bool,
'dot1x_dyn_acl': bool,
'max_lldp_lost': int,
}

Expand Down Expand Up @@ -149,6 +151,7 @@ def __init__(self, _id, dp_id, conf=None):
self.dot1x = None
self.dot1x_acl = None
self.dot1x_mab = None
self.dot1x_dyn_acl = None
self.dp_id = None
self.enabled = None
self.hairpin = None
Expand Down Expand Up @@ -240,6 +243,15 @@ def check_config(self):
if self.dot1x_mab:
test_config_condition(not self.dot1x, (
'802.1x_MAB requires dot1x to be enabled on the port also'))
test_config_condition(self.dot1x_dyn_acl, (
'802.1x_ACL cannot be used with 802.1x_DYN_ACL'))
if self.dot1x_dyn_acl:
test_config_condition(not self.dot1x, (
'802.1x_DYN_ACL requires dot1x to be enabled also'))
test_config_condition(self.dot1x_acl, (
'802.1x_DYN_ACL cannot be used with 802.1x_ACL'))
test_config_condition(self.dot1x_acl, (
'802.1x_DYN_ACL cannot be used with 802.1x_MAB'))
if self.mirror:
test_config_condition(self.tagged_vlans or self.native_vlan, (
'mirror port %s cannot have any VLANs assigned' % self))
Expand Down
24 changes: 13 additions & 11 deletions faucet/valve_acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ def build_output_actions(acl_table, output_dict):

# TODO: change this, maybe this can be rewritten easily
# possibly replace with a class for ACLs
def build_acl_entry(acl_table, rule_conf, meters,
acl_allow_inst, acl_force_port_vlan_inst,
port_num=None, vlan_vid=None):
def build_acl_entry( # pylint: disable=too-many-arguments,too-many-branches,too-many-statements
acl_table, rule_conf, meters,
acl_allow_inst, acl_force_port_vlan_inst,
port_num=None, vlan_vid=None):
"""Build flow/groupmods for one ACL rule entry."""
acl_inst = []
acl_act = []
Expand Down Expand Up @@ -180,15 +181,15 @@ def build_acl_ofmsgs(acls, acl_table,
return ofmsgs


def build_acl_port_of_msgs(acl, vid, port_num, acl_table, goto_table):
def build_acl_port_of_msgs(acl, vid, port_num, acl_table, goto_table, priority):
'''A Helper function for building Openflow Mod Messages for Port ACLs'''
ofmsgs = None
if acl.rules:
ofmsgs = build_acl_ofmsgs(
[acl], acl_table,
[valve_of.goto_table(goto_table)],
[valve_of.goto_table(goto_table)],
2 ** 16 - 1, acl.meter, acl.exact_match,
priority, acl.meter, acl.exact_match,
vlan_vid=vid, port_num=port_num)
return ofmsgs

Expand All @@ -215,6 +216,7 @@ def __init__(self, port_acl_table, vlan_acl_table, egress_acl_table,
self.vlan_acl_table = vlan_acl_table
self.egress_acl_table = egress_acl_table
self.pipeline = pipeline
# TODO Rename priorities
self.acl_priority = self._FILTER_PRIORITY
self.dot1x_static_rules_priority = self.acl_priority + 1
self.auth_priority = self._HIGH_PRIORITY
Expand Down Expand Up @@ -298,17 +300,17 @@ def add_authed_mac(self, port_num, mac):
priority=self.auth_priority,
inst=self.pipeline.accept_to_vlan())]

def del_authed_mac(self, port_num, mac=None):
def del_authed_mac(self, port_num, mac=None, strict=True):
"""remove authed mac address"""
if mac:
return [self.port_acl_table.flowdel(
self.port_acl_table.match(in_port=port_num, eth_src=mac),
priority=self.auth_priority,
strict=True)]
strict=strict)]
return [self.port_acl_table.flowdel(
self.port_acl_table.match(in_port=port_num),
priority=self.auth_priority,
strict=True)]
strict=strict)]

def del_port_acl(self, acl, port_num, mac=None):
"""Delete ACL rules for Port"""
Expand All @@ -322,7 +324,7 @@ def convert_to_flow_del(ofp_flowmods):

pipeline_vlan_table = self.pipeline.vlan_table
flowmods = build_acl_port_of_msgs(acl, None, port_num, self.port_acl_table,
pipeline_vlan_table)
pipeline_vlan_table, self.auth_priority)
for flow in flowmods:
flow.match = add_mac_address_to_match(flow.match, mac)

Expand All @@ -331,8 +333,8 @@ def convert_to_flow_del(ofp_flowmods):
def add_port_acl(self, acl, port_num, mac=None):
"""Create ACL openflow rules for Port"""
pipeline_vlan_table = self.pipeline.vlan_table
flowmods = build_acl_port_of_msgs(acl, None, port_num,
self.port_acl_table, pipeline_vlan_table)
flowmods = build_acl_port_of_msgs(acl, None, port_num, self.port_acl_table,
pipeline_vlan_table, self.auth_priority)

for flow in flowmods:
flow.match = add_mac_address_to_match(flow.match, mac)
Expand Down
Loading

0 comments on commit d4ff92d

Please sign in to comment.