Charles Chan
Committed by Jonathan Hart

CORD-73 Change the way we configure host learning in segment routing

hostLearning config
    - true: enable host learning
    - false or not provided: disable host learning
suppressHost config
    - all connect points listed here will be ignored
    - no effect if hostLearning is false
    - accept all hosts if this config is not provided

Change-Id: Id4a60bd47cac1f226ab8ba5391931ad2fb798529
...@@ -5,6 +5,7 @@ COMPILE_DEPS = [ ...@@ -5,6 +5,7 @@ COMPILE_DEPS = [
5 '//cli:onos-cli', 5 '//cli:onos-cli',
6 '//core/store/serializers:onos-core-serializers', 6 '//core/store/serializers:onos-core-serializers',
7 '//incubator/api:onos-incubator-api', 7 '//incubator/api:onos-incubator-api',
8 + '//providers/netcfghost:onos-providers-netcfghost',
8 '//utils/rest:onlab-rest', 9 '//utils/rest:onlab-rest',
9 ] 10 ]
10 11
......
...@@ -76,6 +76,11 @@ ...@@ -76,6 +76,11 @@
76 <version>${project.version}</version> 76 <version>${project.version}</version>
77 </dependency> 77 </dependency>
78 <dependency> 78 <dependency>
79 + <groupId>org.onosproject</groupId>
80 + <artifactId>onos-netcfg-host-provider</artifactId>
81 + <version>${project.version}</version>
82 + </dependency>
83 + <dependency>
79 <groupId>javax.ws.rs</groupId> 84 <groupId>javax.ws.rs</groupId>
80 <artifactId>javax.ws.rs-api</artifactId> 85 <artifactId>javax.ws.rs-api</artifactId>
81 <version>2.0.1</version> 86 <version>2.0.1</version>
......
...@@ -24,6 +24,7 @@ import org.onlab.packet.MacAddress; ...@@ -24,6 +24,7 @@ import org.onlab.packet.MacAddress;
24 import org.onlab.packet.VlanId; 24 import org.onlab.packet.VlanId;
25 import org.onosproject.net.ConnectPoint; 25 import org.onosproject.net.ConnectPoint;
26 import org.onosproject.net.DeviceId; 26 import org.onosproject.net.DeviceId;
27 +import org.onosproject.net.Host;
27 import org.onosproject.net.HostLocation; 28 import org.onosproject.net.HostLocation;
28 import org.onosproject.net.PortNumber; 29 import org.onosproject.net.PortNumber;
29 import org.onosproject.net.flow.DefaultTrafficSelector; 30 import org.onosproject.net.flow.DefaultTrafficSelector;
...@@ -37,6 +38,8 @@ import org.onosproject.net.flowobjective.ForwardingObjective; ...@@ -37,6 +38,8 @@ import org.onosproject.net.flowobjective.ForwardingObjective;
37 import org.onosproject.net.flowobjective.ObjectiveContext; 38 import org.onosproject.net.flowobjective.ObjectiveContext;
38 import org.onosproject.net.host.HostEvent; 39 import org.onosproject.net.host.HostEvent;
39 import org.onosproject.net.host.HostService; 40 import org.onosproject.net.host.HostService;
41 +import org.onosproject.provider.netcfghost.NetworkConfigHostProvider;
42 +import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
40 import org.slf4j.Logger; 43 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory; 44 import org.slf4j.LoggerFactory;
42 45
...@@ -69,23 +72,24 @@ public class HostHandler { ...@@ -69,23 +72,24 @@ public class HostHandler {
69 if (!deviceId.equals(devId)) { 72 if (!deviceId.equals(devId)) {
70 return; 73 return;
71 } 74 }
72 - processHostAddedEventInternal(host.mac(), host.vlan(), 75 + processHostAddedEventInternal(host);
73 - host.location(), host.ipAddresses());
74 }); 76 });
75 } 77 }
76 78
77 protected void processHostAddedEvent(HostEvent event) { 79 protected void processHostAddedEvent(HostEvent event) {
78 - processHostAddedEventInternal(event.subject().mac(), event.subject().vlan(), 80 + processHostAddedEventInternal(event.subject());
79 - event.subject().location(), event.subject().ipAddresses());
80 } 81 }
81 82
82 - private void processHostAddedEventInternal(MacAddress mac, VlanId vlanId, 83 + private void processHostAddedEventInternal(Host host) {
83 - HostLocation location, Set<IpAddress> ips) { 84 + MacAddress mac = host.mac();
85 + VlanId vlanId = host.vlan();
86 + HostLocation location = host.location();
84 DeviceId deviceId = location.deviceId(); 87 DeviceId deviceId = location.deviceId();
85 PortNumber port = location.port(); 88 PortNumber port = location.port();
86 - log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port); 89 + Set<IpAddress> ips = host.ipAddresses();
90 + log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
87 91
88 - if (!srManager.deviceConfiguration.suppressHost().contains(location)) { 92 + if (accepted(host)) {
89 // Populate bridging table entry 93 // Populate bridging table entry
90 log.debug("Populate L2 table entry for host {} at {}:{}", 94 log.debug("Populate L2 table entry for host {} at {}:{}",
91 mac, deviceId, port); 95 mac, deviceId, port);
...@@ -121,8 +125,7 @@ public class HostHandler { ...@@ -121,8 +125,7 @@ public class HostHandler {
121 Set<IpAddress> ips = event.subject().ipAddresses(); 125 Set<IpAddress> ips = event.subject().ipAddresses();
122 log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port); 126 log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
123 127
124 - if (!srManager.deviceConfiguration.suppressHost() 128 + if (accepted(event.subject())) {
125 - .contains(new ConnectPoint(deviceId, port))) {
126 // Revoke bridging table entry 129 // Revoke bridging table entry
127 ForwardingObjective.Builder fob = 130 ForwardingObjective.Builder fob =
128 hostFwdObjBuilder(deviceId, mac, vlanId, port); 131 hostFwdObjBuilder(deviceId, mac, vlanId, port);
...@@ -161,8 +164,7 @@ public class HostHandler { ...@@ -161,8 +164,7 @@ public class HostHandler {
161 log.debug("Host {}/{} is moved from {}:{} to {}:{}", 164 log.debug("Host {}/{} is moved from {}:{} to {}:{}",
162 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort); 165 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
163 166
164 - if (!srManager.deviceConfiguration.suppressHost() 167 + if (accepted(event.prevSubject())) {
165 - .contains(new ConnectPoint(prevDeviceId, prevPort))) {
166 // Revoke previous bridging table entry 168 // Revoke previous bridging table entry
167 ForwardingObjective.Builder prevFob = 169 ForwardingObjective.Builder prevFob =
168 hostFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort); 170 hostFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
...@@ -186,8 +188,7 @@ public class HostHandler { ...@@ -186,8 +188,7 @@ public class HostHandler {
186 }); 188 });
187 } 189 }
188 190
189 - if (!srManager.deviceConfiguration.suppressHost() 191 + if (accepted(event.subject())) {
190 - .contains(new ConnectPoint(newDeviceId, newPort))) {
191 // Populate new bridging table entry 192 // Populate new bridging table entry
192 ForwardingObjective.Builder newFob = 193 ForwardingObjective.Builder newFob =
193 hostFwdObjBuilder(newDeviceId, mac, vlanId, newPort); 194 hostFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
...@@ -225,8 +226,7 @@ public class HostHandler { ...@@ -225,8 +226,7 @@ public class HostHandler {
225 Set<IpAddress> newIps = event.subject().ipAddresses(); 226 Set<IpAddress> newIps = event.subject().ipAddresses();
226 log.debug("Host {}/{} is updated", mac, vlanId); 227 log.debug("Host {}/{} is updated", mac, vlanId);
227 228
228 - if (!srManager.deviceConfiguration.suppressHost() 229 + if (accepted(event.prevSubject())) {
229 - .contains(new ConnectPoint(prevDeviceId, prevPort))) {
230 // Revoke previous IP table entry 230 // Revoke previous IP table entry
231 prevIps.forEach(ip -> { 231 prevIps.forEach(ip -> {
232 if (ip.isIp4()) { 232 if (ip.isIp4()) {
...@@ -237,8 +237,7 @@ public class HostHandler { ...@@ -237,8 +237,7 @@ public class HostHandler {
237 }); 237 });
238 } 238 }
239 239
240 - if (!srManager.deviceConfiguration.suppressHost() 240 + if (accepted(event.subject())) {
241 - .contains(new ConnectPoint(newDeviceId, newPort))) {
242 // Populate new IP table entry 241 // Populate new IP table entry
243 newIps.forEach(ip -> { 242 newIps.forEach(ip -> {
244 if (ip.isIp4()) { 243 if (ip.isIp4()) {
...@@ -344,4 +343,27 @@ public class HostHandler { ...@@ -344,4 +343,27 @@ public class HostHandler {
344 srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(ip4Prefix)); 343 srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(ip4Prefix));
345 } 344 }
346 } 345 }
346 +
347 + /**
348 + * Check if a host is accepted or not.
349 + *
350 + * @param host host to be checked
351 + * @return true if segment routing accepts the host
352 + */
353 + private boolean accepted(Host host) {
354 + // Always accept configured hosts
355 + if (host.providerId().equals(NetworkConfigHostProvider.PROVIDER_ID)) {
356 + return true;
357 + }
358 +
359 + SegmentRoutingAppConfig appConfig = srManager.cfgService
360 + .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
361 + boolean accepted = appConfig != null &&
362 + appConfig.hostLearning() &&
363 + !appConfig.suppressHost().contains(host.location());
364 + if (!accepted) {
365 + log.info("Ignore suppressed host {}", host.id());
366 + }
367 + return accepted;
368 + }
347 } 369 }
......
...@@ -51,6 +51,7 @@ import org.onosproject.net.flowobjective.ObjectiveContext; ...@@ -51,6 +51,7 @@ import org.onosproject.net.flowobjective.ObjectiveContext;
51 import org.onosproject.net.mcast.McastEvent; 51 import org.onosproject.net.mcast.McastEvent;
52 import org.onosproject.net.mcast.McastRouteInfo; 52 import org.onosproject.net.mcast.McastRouteInfo;
53 import org.onosproject.net.topology.TopologyService; 53 import org.onosproject.net.topology.TopologyService;
54 +import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
54 import org.onosproject.segmentrouting.storekey.McastStoreKey; 55 import org.onosproject.segmentrouting.storekey.McastStoreKey;
55 import org.onosproject.store.serializers.KryoNamespaces; 56 import org.onosproject.store.serializers.KryoNamespaces;
56 import org.onosproject.store.service.ConsistentMap; 57 import org.onosproject.store.service.ConsistentMap;
...@@ -345,11 +346,11 @@ public class McastHandler { ...@@ -345,11 +346,11 @@ public class McastHandler {
345 */ 346 */
346 private void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan) { 347 private void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan) {
347 // Do nothing if the port is configured as suppressed 348 // Do nothing if the port is configured as suppressed
348 - ConnectPoint connectPt = new ConnectPoint(deviceId, port); 349 + ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
349 - if (srManager.deviceConfiguration == null || 350 + SegmentRoutingAppConfig appConfig = srManager.cfgService
350 - srManager.deviceConfiguration.suppressSubnet().contains(connectPt) || 351 + .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
351 - srManager.deviceConfiguration.suppressHost().contains(connectPt)) { 352 + if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
352 - log.info("Ignore suppressed port {}", connectPt); 353 + log.info("Ignore suppressed port {}", connectPoint);
353 return; 354 return;
354 } 355 }
355 356
......
...@@ -28,6 +28,7 @@ import org.onosproject.net.flowobjective.DefaultObjectiveContext; ...@@ -28,6 +28,7 @@ import org.onosproject.net.flowobjective.DefaultObjectiveContext;
28 import org.onosproject.net.flowobjective.ObjectiveContext; 28 import org.onosproject.net.flowobjective.ObjectiveContext;
29 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; 29 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
30 import org.onosproject.segmentrouting.config.DeviceConfiguration; 30 import org.onosproject.segmentrouting.config.DeviceConfiguration;
31 +import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
31 import org.onosproject.segmentrouting.grouphandler.NeighborSet; 32 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
32 import org.onosproject.net.DeviceId; 33 import org.onosproject.net.DeviceId;
33 import org.onosproject.net.Port; 34 import org.onosproject.net.Port;
...@@ -540,7 +541,9 @@ public class RoutingRulePopulator { ...@@ -540,7 +541,9 @@ public class RoutingRulePopulator {
540 for (Port port : devPorts) { 541 for (Port port : devPorts) {
541 ConnectPoint connectPoint = new ConnectPoint(deviceId, port.number()); 542 ConnectPoint connectPoint = new ConnectPoint(deviceId, port.number());
542 // TODO: Handles dynamic port events when we are ready for dynamic config 543 // TODO: Handles dynamic port events when we are ready for dynamic config
543 - if (!srManager.deviceConfiguration.suppressSubnet().contains(connectPoint) && 544 + SegmentRoutingAppConfig appConfig = srManager.cfgService
545 + .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
546 + if ((appConfig == null || !appConfig.suppressSubnet().contains(connectPoint)) &&
544 port.isEnabled()) { 547 port.isEnabled()) {
545 Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number()); 548 Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
546 VlanId assignedVlan = (portSubnet == null) 549 VlanId assignedVlan = (portSubnet == null)
......
...@@ -113,7 +113,10 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -113,7 +113,10 @@ public class DeviceConfiguration implements DeviceProperties {
113 cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class); 113 cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class);
114 portSubjects.forEach(subject -> { 114 portSubjects.forEach(subject -> {
115 // Do not process excluded ports 115 // Do not process excluded ports
116 - if (suppressSubnet().contains(subject)) { 116 + SegmentRoutingAppConfig appConfig =
117 + cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
118 + if (appConfig != null && appConfig.suppressSubnet().contains(subject)) {
119 + log.info("Ignore suppressed port {}", subject);
117 return; 120 return;
118 } 121 }
119 122
...@@ -498,28 +501,6 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -498,28 +501,6 @@ public class DeviceConfiguration implements DeviceProperties {
498 } 501 }
499 502
500 /** 503 /**
501 - * Gets connect points for which segment routing does not install subnet rules.
502 - *
503 - * @return set of connect points
504 - */
505 - public Set<ConnectPoint> suppressSubnet() {
506 - SegmentRoutingAppConfig appConfig =
507 - cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
508 - return (appConfig != null) ? appConfig.suppressSubnet() : ImmutableSet.of();
509 - }
510 -
511 - /**
512 - * Gets connect points for which segment routing does not install host rules.
513 - *
514 - * @return set of connect points
515 - */
516 - public Set<ConnectPoint> suppressHost() {
517 - SegmentRoutingAppConfig appConfig =
518 - cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
519 - return (appConfig != null) ? appConfig.suppressHost() : ImmutableSet.of();
520 - }
521 -
522 - /**
523 * Add subnet to specific connect point. 504 * Add subnet to specific connect point.
524 * 505 *
525 * @param cp connect point 506 * @param cp connect point
......
...@@ -38,10 +38,12 @@ public class SegmentRoutingAppConfig extends Config<ApplicationId> { ...@@ -38,10 +38,12 @@ public class SegmentRoutingAppConfig extends Config<ApplicationId> {
38 private static final String VROUTER_ID = "vRouterId"; 38 private static final String VROUTER_ID = "vRouterId";
39 private static final String SUPPRESS_SUBNET = "suppressSubnet"; 39 private static final String SUPPRESS_SUBNET = "suppressSubnet";
40 private static final String SUPPRESS_HOST = "suppressHost"; 40 private static final String SUPPRESS_HOST = "suppressHost";
41 + private static final String HOST_LEARNING = "hostLearning";
41 42
42 @Override 43 @Override
43 public boolean isValid() { 44 public boolean isValid() {
44 - return hasOnlyFields(VROUTER_MACS, VROUTER_ID, SUPPRESS_SUBNET, SUPPRESS_HOST) && 45 + return hasOnlyFields(VROUTER_MACS, VROUTER_ID, SUPPRESS_SUBNET,
46 + SUPPRESS_HOST, HOST_LEARNING) &&
45 vRouterMacs() != null && vRouterId() != null && 47 vRouterMacs() != null && vRouterId() != null &&
46 suppressSubnet() != null && suppressHost() != null; 48 suppressSubnet() != null && suppressHost() != null;
47 } 49 }
...@@ -225,6 +227,26 @@ public class SegmentRoutingAppConfig extends Config<ApplicationId> { ...@@ -225,6 +227,26 @@ public class SegmentRoutingAppConfig extends Config<ApplicationId> {
225 return this; 227 return this;
226 } 228 }
227 229
230 + /**
231 + * Gets whether host learning is enabled or not.
232 + *
233 + * @return true if enabled. false if disabled or not configured
234 + */
235 + public boolean hostLearning() {
236 + return object.has(HOST_LEARNING) && object.path(HOST_LEARNING).asBoolean();
237 + }
238 +
239 + /**
240 + * Sets whether host learning is enabled or not.
241 + *
242 + * @param enabled true if enabled
243 + * @return this {@link SegmentRoutingAppConfig}
244 + */
245 + public SegmentRoutingAppConfig setHostLearning(boolean enabled) {
246 + object.put(HOST_LEARNING, enabled);
247 + return this;
248 + }
249 +
228 @Override 250 @Override
229 public String toString() { 251 public String toString() {
230 return toStringHelper(this) 252 return toStringHelper(this)
...@@ -232,6 +254,7 @@ public class SegmentRoutingAppConfig extends Config<ApplicationId> { ...@@ -232,6 +254,7 @@ public class SegmentRoutingAppConfig extends Config<ApplicationId> {
232 .add("vRouterId", vRouterId()) 254 .add("vRouterId", vRouterId())
233 .add("suppressSubnet", suppressSubnet()) 255 .add("suppressSubnet", suppressSubnet())
234 .add("suppressHost", suppressHost()) 256 .add("suppressHost", suppressHost())
257 + .add("hostLearning", hostLearning())
235 .toString(); 258 .toString();
236 } 259 }
237 } 260 }
......
...@@ -210,6 +210,27 @@ public class SegmentRoutingAppConfigTest { ...@@ -210,6 +210,27 @@ public class SegmentRoutingAppConfigTest {
210 assertTrue(suppressHost.contains(PORT_3)); 210 assertTrue(suppressHost.contains(PORT_3));
211 } 211 }
212 212
213 + /**
214 + * Tests hostLearning getter.
215 + *
216 + * @throws Exception
217 + */
218 + @Test
219 + public void testHostLearning() throws Exception {
220 + assertFalse(config.hostLearning());
221 + }
222 +
223 + /**
224 + * Tests hostLearning setter.
225 + *
226 + * @throws Exception
227 + */
228 + @Test
229 + public void testSetHostLearning() throws Exception {
230 + config.setHostLearning(true);
231 + assertTrue(config.hostLearning());
232 + }
233 +
213 private class MockDelegate implements ConfigApplyDelegate { 234 private class MockDelegate implements ConfigApplyDelegate {
214 @Override 235 @Override
215 public void onApply(Config config) { 236 public void onApply(Config config) {
......
...@@ -11,5 +11,6 @@ ...@@ -11,5 +11,6 @@
11 "suppressHost" : [ 11 "suppressHost" : [
12 "of:1/1", 12 "of:1/1",
13 "wrongPort" 13 "wrongPort"
14 - ] 14 + ],
15 + "hostLearning" : false
15 } 16 }
......
...@@ -11,5 +11,6 @@ ...@@ -11,5 +11,6 @@
11 "suppressHost" : [ 11 "suppressHost" : [
12 "of:1/1", 12 "of:1/1",
13 "of:1/2" 13 "of:1/2"
14 - ] 14 + ],
15 + "hostLearning" : false
15 } 16 }
......
...@@ -59,8 +59,9 @@ public class NetworkConfigHostProvider extends AbstractProvider implements HostP ...@@ -59,8 +59,9 @@ public class NetworkConfigHostProvider extends AbstractProvider implements HostP
59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
60 protected NetworkConfigRegistry networkConfigRegistry; 60 protected NetworkConfigRegistry networkConfigRegistry;
61 61
62 - private static final String APP_NAME = "org.onosproject.provider.netcfghost";
63 private ApplicationId appId; 62 private ApplicationId appId;
63 + private static final String APP_NAME = "org.onosproject.netcfghost";
64 + public static final ProviderId PROVIDER_ID = new ProviderId("host", APP_NAME);
64 protected HostProviderService providerService; 65 protected HostProviderService providerService;
65 66
66 private final Logger log = LoggerFactory.getLogger(getClass()); 67 private final Logger log = LoggerFactory.getLogger(getClass());
...@@ -71,7 +72,7 @@ public class NetworkConfigHostProvider extends AbstractProvider implements HostP ...@@ -71,7 +72,7 @@ public class NetworkConfigHostProvider extends AbstractProvider implements HostP
71 * Creates an network config host location provider. 72 * Creates an network config host location provider.
72 */ 73 */
73 public NetworkConfigHostProvider() { 74 public NetworkConfigHostProvider() {
74 - super(new ProviderId("host", APP_NAME)); 75 + super(PROVIDER_ID);
75 } 76 }
76 77
77 @Activate 78 @Activate
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #ff2600; -webkit-text-stroke: #ff2600} 15 p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #ff2600; -webkit-text-stroke: #ff2600}
16 p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #000000; -webkit-text-stroke: #000000} 16 p.p7 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #000000; -webkit-text-stroke: #000000}
17 p.p8 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #00c7fc; -webkit-text-stroke: #000000} 17 p.p8 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #00c7fc; -webkit-text-stroke: #000000}
18 + p.p9 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 14.0px; font: 12.0px Menlo; color: #78ba5b; -webkit-text-stroke: #353535}
18 span.s1 {font-kerning: none} 19 span.s1 {font-kerning: none}
19 span.s2 {font-kerning: none; color: #0433ff; -webkit-text-stroke: 0px #0433ff} 20 span.s2 {font-kerning: none; color: #0433ff; -webkit-text-stroke: 0px #0433ff}
20 span.s3 {font-kerning: none; color: #000000; -webkit-text-stroke: 0px #000000} 21 span.s3 {font-kerning: none; color: #000000; -webkit-text-stroke: 0px #000000}
...@@ -24,7 +25,8 @@ ...@@ -24,7 +25,8 @@
24 span.s7 {font-kerning: none; color: #ff40ff; -webkit-text-stroke: 0px #ff40ff} 25 span.s7 {font-kerning: none; color: #ff40ff; -webkit-text-stroke: 0px #ff40ff}
25 span.s8 {font-kerning: none; color: #ff2600; -webkit-text-stroke: 0px #ff2600} 26 span.s8 {font-kerning: none; color: #ff2600; -webkit-text-stroke: 0px #ff2600}
26 span.s9 {font-kerning: none; color: #000000} 27 span.s9 {font-kerning: none; color: #000000}
27 - span.s10 {font-kerning: none; color: #669c35; -webkit-text-stroke: 0px #669c35} 28 + span.s10 {font-kerning: none; -webkit-text-stroke: 0px #000000}
29 + span.s11 {font-kerning: none; color: #669c35; -webkit-text-stroke: 0px #669c35}
28 span.Apple-tab-span {white-space:pre} 30 span.Apple-tab-span {white-space:pre}
29 </style> 31 </style>
30 </head> 32 </head>
...@@ -225,7 +227,8 @@ ...@@ -225,7 +227,8 @@
225 <p class="p4"><span class="s3"><span class="Apple-converted-space">                 </span>"suppressSubnet" : [ </span><span class="s1">// Do not push subnet rules for these ports</span></p> 227 <p class="p4"><span class="s3"><span class="Apple-converted-space">                 </span>"suppressSubnet" : [ </span><span class="s1">// Do not push subnet rules for these ports</span></p>
226 <p class="p7"><span class="s1"><span class="Apple-converted-space">                    </span>"of:0000000000000002/31", "of:0000000000000002/32"</span></p> 228 <p class="p7"><span class="s1"><span class="Apple-converted-space">                    </span>"of:0000000000000002/31", "of:0000000000000002/32"</span></p>
227 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>],</span></p> 229 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>],</span></p>
228 -<p class="p4"><span class="s3"><span class="Apple-converted-space">                </span>"suppressHost" : [ </span><span class="s1">// Do not push host rules for these ports</span></p> 230 +<p class="p9"><span class="s3"><span class="Apple-converted-space">                </span>"hostLearning" : true, </span><span class="s10">// </span><span class="s1">Host learning is enabled if true. Host learning is disabled if false or the config is not provided</span></p>
231 +<p class="p4"><span class="s3"><span class="Apple-converted-space">                </span>"suppressHost" : [ </span><span class="s1">// Hosts on these ports will be ignored. Only takes effect when hostLearning is enabled</span></p>
229 <p class="p7"><span class="s1"><span class="Apple-converted-space">                    </span>"of:0000000000000001/65", "of:0000000000000001/73",</span></p> 232 <p class="p7"><span class="s1"><span class="Apple-converted-space">                    </span>"of:0000000000000001/65", "of:0000000000000001/73",</span></p>
230 <p class="p7"><span class="s1"><span class="Apple-converted-space">                    </span>"of:0000000000000002/31", "of:0000000000000002/32"</span></p> 233 <p class="p7"><span class="s1"><span class="Apple-converted-space">                    </span>"of:0000000000000002/31", "of:0000000000000002/32"</span></p>
231 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>]</span></p> 234 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>]</span></p>
...@@ -236,7 +239,7 @@ ...@@ -236,7 +239,7 @@
236 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"controlPlaneConnectPoint" : "of:0000000000000002/31", </span><span class="s5">// location of Quagga</span></p> 239 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"controlPlaneConnectPoint" : "of:0000000000000002/31", </span><span class="s5">// location of Quagga</span></p>
237 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"ospfEnabled" : "true", </span><span class="s5">// enable OSPF</span></p> 240 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"ospfEnabled" : "true", </span><span class="s5">// enable OSPF</span></p>
238 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"pimEnabled" : "true", </span><span class="s6">// enable PIM</span></p> 241 <p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"pimEnabled" : "true", </span><span class="s6">// enable PIM</span></p>
239 -<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"interfaces" : [ "external-quagga" ] </span><span class="s10">// </span><span class="s5">VR only handles peers on these ports</span></p> 242 +<p class="p7"><span class="s1"><span class="Apple-converted-space">                </span>"interfaces" : [ "external-quagga" ] </span><span class="s11">// </span><span class="s5">VR only handles peers on these ports</span></p>
240 <p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>}</span></p> 243 <p class="p7"><span class="s1"><span class="Apple-converted-space">            </span>}</span></p>
241 <p class="p7"><span class="s1"><span class="Apple-converted-space">        </span>}</span></p> 244 <p class="p7"><span class="s1"><span class="Apple-converted-space">        </span>}</span></p>
242 <p class="p7"><span class="s1"><span class="Apple-converted-space">    </span>}</span></p> 245 <p class="p7"><span class="s1"><span class="Apple-converted-space">    </span>}</span></p>
......
...@@ -234,6 +234,7 @@ ...@@ -234,6 +234,7 @@
234 "suppressSubnet" : [ 234 "suppressSubnet" : [
235 "of:0000000000000002/31", "of:0000000000000002/32" 235 "of:0000000000000002/31", "of:0000000000000002/32"
236 ], 236 ],
237 + "hostLearning" : true,
237 "suppressHost" : [ 238 "suppressHost" : [
238 "of:0000000000000001/65", "of:0000000000000001/73", 239 "of:0000000000000001/65", "of:0000000000000001/73",
239 "of:0000000000000002/31", "of:0000000000000002/32" 240 "of:0000000000000002/31", "of:0000000000000002/32"
......