Skip to content
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

feat: add regex to as path access lists #7250

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ tokens {

ACCESS_LIST
:
'access-list' -> pushMode(M_Word)
'access-list' -> pushMode(M_AccessList)
;

ACTIVATE: 'activate';
Expand Down Expand Up @@ -808,7 +808,7 @@ F_Word
fragment
F_WordChar
:
[0-9A-Za-z!@#$^*_=+.;:{}]
[0-9A-Za-z!@#$^*_=+.;:{}\\[\]|()]
| '-'
;

Expand Down Expand Up @@ -1104,3 +1104,29 @@ M_Update_NEWLINE
F_Newline+ -> type ( NEWLINE ), popMode
;

mode M_AccessList;

M_AccessList_DENY
:
'deny' -> type(DENY)
;

M_AccessList_PERMIT
:
'permit' -> type(PERMIT)
;

M_AsPathAccessList_WORD
:
F_Word -> type( WORD )
;

M_AccessList_NEWLINE
:
F_Newline+ -> type ( NEWLINE ) , popMode
;

M_AccessList_WS
:
F_Whitespace+ -> channel ( HIDDEN )
;
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ statement

ip_as_path
:
AS_PATH ACCESS_LIST name = word action = line_action asn = uint32 NEWLINE
AS_PATH ACCESS_LIST name = word action = access_list_action as_path_regex = word NEWLINE
;

s_agentx
Expand Down Expand Up @@ -185,5 +185,3 @@ s_username
:
USERNAME null_rest_of_line
;


Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ ip_prefix_length
UINT8
;

access_list_action
:
PERMIT
| DENY
;

line_action
:
deny = DENY
Expand Down Expand Up @@ -153,4 +159,4 @@ word
null_rest_of_line
:
~NEWLINE* NEWLINE
;
;
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.batfish.grammar.BatfishCombinedParser;
import org.batfish.grammar.SilentSyntaxListener;
import org.batfish.grammar.UnrecognizedLineToken;
import org.batfish.grammar.cumulus_frr.CumulusFrrParser.Access_list_actionContext;
import org.batfish.grammar.cumulus_frr.CumulusFrrParser.Agg_feature_as_setContext;
import org.batfish.grammar.cumulus_frr.CumulusFrrParser.Agg_feature_matching_med_onlyContext;
import org.batfish.grammar.cumulus_frr.CumulusFrrParser.Agg_feature_originContext;
Expand Down Expand Up @@ -1775,14 +1776,23 @@ public void exitIp_route(Ip_routeContext ctx) {
}
}

private LineAction toLineAction(Access_list_actionContext ctx) {
if (ctx.PERMIT() != null) {
return LineAction.PERMIT;
} else {
assert ctx.DENY() != null;
return LineAction.DENY;
}
}

@Override
public void exitIp_as_path(Ip_as_pathContext ctx) {
String name = ctx.name.getText();
LineAction action = ctx.action.permit != null ? LineAction.PERMIT : LineAction.DENY;
long asNum = toLong(ctx.asn);
LineAction action = toLineAction(ctx.action);
String regex = ctx.as_path_regex.getText();
_frr.getIpAsPathAccessLists()
.computeIfAbsent(name, IpAsPathAccessList::new)
.addLine(new IpAsPathAccessListLine(action, asNum));
.addLine(new IpAsPathAccessListLine(action, regex));
_c.defineStructure(IP_AS_PATH_ACCESS_LIST, name, ctx);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1559,7 +1559,7 @@ static void convertIpCommunityLists(
new CommunityMatchRegex(ColonSeparatedRendering.instance(), toJavaRegex(line.getRegex())));
}

private static @Nonnull String toJavaRegex(String cumulusRegex) {
static @Nonnull String toJavaRegex(String cumulusRegex) {
String withoutQuotes;
if (cumulusRegex.charAt(0) == '"' && cumulusRegex.charAt(cumulusRegex.length() - 1) == '"') {
withoutQuotes = cumulusRegex.substring(1, cumulusRegex.length() - 1);
Expand All @@ -1585,10 +1585,7 @@ static void convertIpAsPathAccessLists(
asPathAccessList.getLines().stream()
// TODO Check FRR AS path match semantics.
// This regex assumes we should match any path containing the specified ASN anywhere.
.map(
line ->
new AsPathAccessListLine(
line.getAction(), String.format("(^| )%s($| )", line.getAsNum())))
.map(IpAsPathAccessListLine::toAsPathAccessListLine)
.collect(ImmutableList.toImmutableList());
return new AsPathAccessList(name, lines);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,30 @@

import java.io.Serializable;
import javax.annotation.Nonnull;
import org.batfish.datamodel.AsPathAccessListLine;
import org.batfish.datamodel.LineAction;

/** Represents one line of a Cumulus AS-path access list. */
public class IpAsPathAccessListLine implements Serializable {
private final @Nonnull LineAction _action;
private final long _asNum;
private final String _regex;

public IpAsPathAccessListLine(@Nonnull LineAction action, long asNum) {
public IpAsPathAccessListLine(@Nonnull LineAction action, String regex) {
_action = action;
_asNum = asNum;
_regex = regex;
}

@Nonnull
public LineAction getAction() {
return _action;
}

public long getAsNum() {
return _asNum;
public String getRegex() {
return _regex;
}

public AsPathAccessListLine toAsPathAccessListLine() {
String regex = CumulusConversions.toJavaRegex(_regex);
return new AsPathAccessListLine(_action, regex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1447,31 +1447,62 @@ public void testCumulusFrrVrfRouteMapSetCommunity() {
@Test
public void testCumulusFrrIpAsPathAccessList() {
String name = "NAME";
long as1 = 11111;
long as2 = 22222;
String as1 = "^11111$";
String as2 = "_1_";
String as3 = "^1[1-2]";
String as4 = "^1(1)";
parse(
String.format(
"ip as-path access-list %s permit %s\n" + "ip as-path access-list %s deny %s\n",
name, as1, name, as2));
"ip as-path access-list %s permit %s\n"
+ "ip as-path access-list %s permit %s\n"
+ "ip as-path access-list %s permit %s\n"
+ "ip as-path access-list %s permit %s\n"
+ "ip as-path access-list %s deny %s\n"
+ "ip as-path access-list %s deny %s\n"
+ "ip as-path access-list %s deny %s\n"
+ "ip as-path access-list %s deny %s\n",
name, as1, name, as2, name, as3, name, as4, name, as1, name, as2, name, as3, name,
as4));

// Check that config has the expected AS-path access list with the expected name and num lines
assertThat(_frr.getIpAsPathAccessLists().keySet(), contains(name));
IpAsPathAccessList asPathAccessList = _frr.getIpAsPathAccessLists().get(name);
assertThat(asPathAccessList.getName(), equalTo(name));
assertThat(asPathAccessList.getLines(), hasSize(2));
assertThat(asPathAccessList.getLines(), hasSize(8));

// Check that lines look as expected
IpAsPathAccessListLine line0 = asPathAccessList.getLines().get(0);
IpAsPathAccessListLine line1 = asPathAccessList.getLines().get(1);
IpAsPathAccessListLine line2 = asPathAccessList.getLines().get(2);
IpAsPathAccessListLine line3 = asPathAccessList.getLines().get(3);
IpAsPathAccessListLine line4 = asPathAccessList.getLines().get(4);
IpAsPathAccessListLine line5 = asPathAccessList.getLines().get(5);
IpAsPathAccessListLine line6 = asPathAccessList.getLines().get(6);
IpAsPathAccessListLine line7 = asPathAccessList.getLines().get(7);

assertThat(line0.getAction(), equalTo(LineAction.PERMIT));
assertThat(line1.getAction(), equalTo(LineAction.DENY));
assertThat(line0.getAsNum(), equalTo(as1));
assertThat(line1.getAsNum(), equalTo(as2));
assertThat(line1.getAction(), equalTo(LineAction.PERMIT));
assertThat(line2.getAction(), equalTo(LineAction.PERMIT));
assertThat(line3.getAction(), equalTo(LineAction.PERMIT));
assertThat(line4.getAction(), equalTo(LineAction.DENY));
assertThat(line5.getAction(), equalTo(LineAction.DENY));
assertThat(line6.getAction(), equalTo(LineAction.DENY));
assertThat(line7.getAction(), equalTo(LineAction.DENY));

assertThat(line0.getRegex(), equalTo(as1));
assertThat(line1.getRegex(), equalTo(as2));
assertThat(line2.getRegex(), equalTo(as3));
assertThat(line3.getRegex(), equalTo(as4));
assertThat(line4.getRegex(), equalTo(as1));
assertThat(line5.getRegex(), equalTo(as2));
assertThat(line6.getRegex(), equalTo(as3));
assertThat(line7.getRegex(), equalTo(as4));

// Check that the AS-path access list definition was registered
DefinedStructureInfo definedStructureInfo =
getDefinedStructureInfo(CumulusStructureType.IP_AS_PATH_ACCESS_LIST, name);
assertThat(definedStructureInfo.getDefinitionLines().enumerate(), contains(1, 2));
assertThat(
definedStructureInfo.getDefinitionLines().enumerate(), contains(1, 2, 3, 4, 5, 6, 7, 8));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,37 +258,41 @@ public void testToRouteFilter() {

@Test
public void testToAsPathAccessList() {
long permitted = 11111;
long denied = 22222;
String permitted_regex = "(^| )11111($| )";
String denied_regex = "(^| )22222($| )";

IpAsPathAccessList asPathAccessList = new IpAsPathAccessList("name");
asPathAccessList.addLine(new IpAsPathAccessListLine(LineAction.DENY, denied));
asPathAccessList.addLine(new IpAsPathAccessListLine(LineAction.PERMIT, permitted));
asPathAccessList.addLine(new IpAsPathAccessListLine(LineAction.DENY, denied_regex));
asPathAccessList.addLine(new IpAsPathAccessListLine(LineAction.PERMIT, permitted_regex));
AsPathAccessList viList = toAsPathAccessList(asPathAccessList);

// Cache initialization only happens in AsPathAccessList on deserialization o.O
viList = SerializationUtils.clone(viList);

List<AsPathAccessListLine> expectedViLines =
ImmutableList.of(
new AsPathAccessListLine(LineAction.DENY, String.format("(^| )%s($| )", denied)),
new AsPathAccessListLine(LineAction.PERMIT, String.format("(^| )%s($| )", permitted)));
new AsPathAccessListLine(LineAction.DENY, denied_regex),
new AsPathAccessListLine(LineAction.PERMIT, permitted_regex));
assertThat(viList, equalTo(new AsPathAccessList("name", expectedViLines)));

// Matches paths containing permitted ASN
long other = 33333;
assertTrue(viList.permits(AsPath.ofSingletonAsSets(permitted)));
assertTrue(viList.permits(AsPath.ofSingletonAsSets(permitted, other)));
assertTrue(viList.permits(AsPath.ofSingletonAsSets(other, permitted)));
assertTrue(viList.permits(AsPath.ofSingletonAsSets(other, permitted, other)));
long permitted_asn = 11111;
long denied_asn = 22222;
long other_asn = 33333;

assertTrue(viList.permits(AsPath.ofSingletonAsSets(permitted_asn)));
assertTrue(viList.permits(AsPath.ofSingletonAsSets(permitted_asn, other_asn)));
assertTrue(viList.permits(AsPath.ofSingletonAsSets(other_asn, permitted_asn)));
assertTrue(viList.permits(AsPath.ofSingletonAsSets(other_asn, permitted_asn, other_asn)));

// Does not match if denied ASN is in path, even if permitted is also there
assertFalse(viList.permits(AsPath.ofSingletonAsSets(denied)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(denied, permitted)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(permitted, denied)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(permitted, denied, permitted)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(denied_asn)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(denied_asn, permitted_asn)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(permitted_asn, denied_asn)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(permitted_asn, denied_asn, permitted_asn)));

// Does not match by default
assertFalse(viList.permits(AsPath.ofSingletonAsSets(other)));
assertFalse(viList.permits(AsPath.ofSingletonAsSets(other_asn)));
}

@Test
Expand Down