blob: 9780435bd8b6567a976796c95151b82283ad88a7 [file] [log] [blame]
Jian Li4aa17642019-01-30 00:01:11 +09001/*
2 * Copyright 2019-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.k8snetworking.impl;
17
Jian Lif4818b02020-11-04 15:58:18 +090018import org.apache.commons.net.util.SubnetUtils;
Jian Li4aa17642019-01-30 00:01:11 +090019import org.onlab.packet.ARP;
20import org.onlab.packet.EthType;
21import org.onlab.packet.Ethernet;
22import org.onlab.packet.Ip4Address;
23import org.onlab.packet.IpAddress;
Jian Lif4818b02020-11-04 15:58:18 +090024import org.onlab.packet.IpPrefix;
Jian Li4aa17642019-01-30 00:01:11 +090025import org.onlab.packet.MacAddress;
Jian Li44c2b122019-05-03 14:46:34 +090026import org.onlab.packet.VlanId;
27import org.onlab.util.KryoNamespace;
Jian Li4aa17642019-01-30 00:01:11 +090028import org.onlab.util.Tools;
29import org.onosproject.cfg.ComponentConfigService;
30import org.onosproject.cfg.ConfigProperty;
31import org.onosproject.cluster.ClusterService;
32import org.onosproject.cluster.LeadershipService;
33import org.onosproject.cluster.NodeId;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
36import org.onosproject.k8snetworking.api.K8sFlowRuleService;
Jian Li140d8a22019-04-24 23:41:44 +090037import org.onosproject.k8snetworking.api.K8sNetwork;
Jian Li4aa17642019-01-30 00:01:11 +090038import org.onosproject.k8snetworking.api.K8sNetworkService;
39import org.onosproject.k8snetworking.api.K8sPort;
Jian Li7d111d72019-04-12 13:58:44 +090040import org.onosproject.k8snetworking.api.K8sServiceService;
Jian Li2e04e792020-09-11 03:29:16 +090041import org.onosproject.k8snode.api.K8sHostService;
Jian Li4aa17642019-01-30 00:01:11 +090042import org.onosproject.k8snode.api.K8sNode;
Jian Lif4818b02020-11-04 15:58:18 +090043import org.onosproject.k8snode.api.K8sNodeAdminService;
Jian Li4aa17642019-01-30 00:01:11 +090044import org.onosproject.k8snode.api.K8sNodeEvent;
Jian Lif4818b02020-11-04 15:58:18 +090045import org.onosproject.k8snode.api.K8sNodeInfo;
Jian Li4aa17642019-01-30 00:01:11 +090046import org.onosproject.k8snode.api.K8sNodeListener;
Jian Li4aa17642019-01-30 00:01:11 +090047import org.onosproject.mastership.MastershipService;
Jian Li44c2b122019-05-03 14:46:34 +090048import org.onosproject.net.ConnectPoint;
Jian Li2e04e792020-09-11 03:29:16 +090049import org.onosproject.net.DeviceId;
Jian Li4aa17642019-01-30 00:01:11 +090050import org.onosproject.net.PortNumber;
51import org.onosproject.net.device.DeviceService;
52import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
54import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flow.TrafficTreatment;
56import org.onosproject.net.packet.DefaultOutboundPacket;
57import org.onosproject.net.packet.PacketContext;
58import org.onosproject.net.packet.PacketProcessor;
59import org.onosproject.net.packet.PacketService;
Jian Li44c2b122019-05-03 14:46:34 +090060import org.onosproject.store.serializers.KryoNamespaces;
61import org.onosproject.store.service.ConsistentMap;
62import org.onosproject.store.service.Serializer;
63import org.onosproject.store.service.StorageService;
Jian Li4aa17642019-01-30 00:01:11 +090064import org.osgi.service.component.ComponentContext;
65import org.osgi.service.component.annotations.Activate;
66import org.osgi.service.component.annotations.Component;
67import org.osgi.service.component.annotations.Deactivate;
68import org.osgi.service.component.annotations.Modified;
69import org.osgi.service.component.annotations.Reference;
70import org.osgi.service.component.annotations.ReferenceCardinality;
71import org.slf4j.Logger;
72import org.slf4j.LoggerFactory;
73
74import java.nio.ByteBuffer;
75import java.util.Dictionary;
76import java.util.Objects;
77import java.util.Set;
78import java.util.concurrent.ExecutorService;
Jian Li7d111d72019-04-12 13:58:44 +090079import java.util.stream.Collectors;
Jian Li4aa17642019-01-30 00:01:11 +090080
81import static java.util.concurrent.Executors.newSingleThreadExecutor;
Jian Lif4818b02020-11-04 15:58:18 +090082import static org.onlab.packet.IpAddress.Version.INET;
Jian Li4aa17642019-01-30 00:01:11 +090083import static org.onlab.util.Tools.groupedThreads;
84import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
85import static org.onosproject.k8snetworking.api.Constants.ARP_PROXY_MODE;
86import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
87import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Lif4818b02020-11-04 15:58:18 +090088import static org.onosproject.k8snetworking.api.Constants.NODE_FAKE_IP_STR;
89import static org.onosproject.k8snetworking.api.Constants.NODE_FAKE_MAC_STR;
Jian Li140d8a22019-04-24 23:41:44 +090090import static org.onosproject.k8snetworking.api.Constants.NODE_IP_PREFIX;
Jian Li4aa17642019-01-30 00:01:11 +090091import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li7d111d72019-04-12 13:58:44 +090092import static org.onosproject.k8snetworking.api.Constants.SERVICE_FAKE_MAC_STR;
Jian Li2e04e792020-09-11 03:29:16 +090093import static org.onosproject.k8snetworking.api.Constants.SHIFTED_IP_PREFIX;
Jian Li4aa17642019-01-30 00:01:11 +090094import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE;
95import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
96import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
97import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
Jian Li2e04e792020-09-11 03:29:16 +090098import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.allK8sDevices;
Jian Lif4818b02020-11-04 15:58:18 +090099import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getGatewayIp;
Jian Li4aa17642019-01-30 00:01:11 +0900100import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
Jian Li004526d2019-02-25 16:26:27 +0900101import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.unshiftIpDomain;
Jian Li4aa17642019-01-30 00:01:11 +0900102
103/**
104 * Handles ARP packet from containers.
105 */
106@Component(
107 immediate = true,
108 property = {
109 GATEWAY_MAC + "=" + GATEWAY_MAC_DEFAULT,
110 ARP_MODE + "=" + ARP_MODE_DEFAULT
111 }
112)
113public class K8sSwitchingArpHandler {
114
115 private final Logger log = LoggerFactory.getLogger(getClass());
116
Jian Li7d111d72019-04-12 13:58:44 +0900117 private static final String GATEWAY_MAC = "gatewayMac";
118 private static final String ARP_MODE = "arpMode";
119
Jian Li44c2b122019-05-03 14:46:34 +0900120 private static final KryoNamespace SERIALIZER_HOST_MAC = KryoNamespace.newBuilder()
121 .register(KryoNamespaces.API)
122 .build();
123
Jian Li4aa17642019-01-30 00:01:11 +0900124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
125 protected CoreService coreService;
126
127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
128 protected PacketService packetService;
129
130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
131 protected ComponentConfigService configService;
132
133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
134 protected ClusterService clusterService;
135
136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
137 protected LeadershipService leadershipService;
138
139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
140 protected DeviceService deviceService;
141
142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
143 protected MastershipService mastershipService;
144
145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li44c2b122019-05-03 14:46:34 +0900146 protected StorageService storageService;
147
148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lif4818b02020-11-04 15:58:18 +0900149 protected K8sNodeAdminService k8sNodeService;
Jian Li4aa17642019-01-30 00:01:11 +0900150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li2e04e792020-09-11 03:29:16 +0900152 protected K8sHostService k8sHostService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li4aa17642019-01-30 00:01:11 +0900155 protected K8sNetworkService k8sNetworkService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY)
158 protected K8sFlowRuleService k8sFlowRuleService;
159
Jian Li7d111d72019-04-12 13:58:44 +0900160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
161 protected K8sServiceService k8sServiceService;
162
Jian Li4aa17642019-01-30 00:01:11 +0900163 /** Fake MAC address for virtual network subnet gateway. */
164 private String gatewayMac = GATEWAY_MAC_DEFAULT;
165
166 /** ARP processing mode, broadcast | proxy (default). */
167 protected String arpMode = ARP_MODE_DEFAULT;
168
Jian Li44c2b122019-05-03 14:46:34 +0900169 private ConsistentMap<IpAddress, MacAddress> extHostMacStore;
170
Jian Li4aa17642019-01-30 00:01:11 +0900171 private final ExecutorService eventExecutor = newSingleThreadExecutor(
172 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
173
174 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
175 private final InternalNodeEventListener k8sNodeListener = new InternalNodeEventListener();
176
177 private ApplicationId appId;
178 private NodeId localNodeId;
179
180 @Activate
181 void activate() {
182 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
183 configService.registerProperties(getClass());
184 localNodeId = clusterService.getLocalNode().id();
185 leadershipService.runForLeadership(appId.name());
186 k8sNodeService.addListener(k8sNodeListener);
187 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
188
Jian Li44c2b122019-05-03 14:46:34 +0900189 extHostMacStore = storageService.<IpAddress, MacAddress>consistentMapBuilder()
190 .withSerializer(Serializer.using(SERIALIZER_HOST_MAC))
191 .withName("k8s-host-mac-store")
192 .withApplicationId(appId)
193 .build();
194
Jian Li4aa17642019-01-30 00:01:11 +0900195 log.info("Started");
196 }
197
198 @Deactivate
199 void deactivate() {
200 packetService.removeProcessor(packetProcessor);
201 k8sNodeService.removeListener(k8sNodeListener);
202 leadershipService.withdraw(appId.name());
203 configService.unregisterProperties(getClass(), false);
204 eventExecutor.shutdown();
205
206 log.info("Stopped");
207 }
208
209 @Modified
210 void modified(ComponentContext context) {
211 readComponentConfiguration(context);
212
213 log.info("Modified");
214 }
215
216 /**
217 * Processes ARP request packets.
218 *
219 * @param context packet context
220 * @param ethPacket ethernet packet
221 */
222 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
223 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
224 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
225 return;
226 }
227
Jian Li2e04e792020-09-11 03:29:16 +0900228 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
229
230 if (!allK8sDevices(k8sNodeService, k8sHostService).contains(deviceId)) {
231 return;
232 }
233
Jian Li4aa17642019-01-30 00:01:11 +0900234 ARP arpPacket = (ARP) ethPacket.getPayload();
Jian Li44c2b122019-05-03 14:46:34 +0900235 if (arpPacket.getOpCode() == ARP.OP_REQUEST) {
236 processArpRequest(context, ethPacket);
237 } else if (arpPacket.getOpCode() == ARP.OP_REPLY) {
238 processArpReply(context, ethPacket);
Jian Li4aa17642019-01-30 00:01:11 +0900239 }
Jian Li44c2b122019-05-03 14:46:34 +0900240 }
Jian Li4aa17642019-01-30 00:01:11 +0900241
Jian Li44c2b122019-05-03 14:46:34 +0900242 private void processArpRequest(PacketContext context, Ethernet ethPacket) {
243 ARP arpPacket = (ARP) ethPacket.getPayload();
Jian Li07c27f32020-10-08 02:57:45 +0900244 K8sPort srcK8sPort = k8sNetworkService.ports().stream()
Jian Li4aa17642019-01-30 00:01:11 +0900245 .filter(p -> p.macAddress().equals(ethPacket.getSourceMAC()))
246 .findAny().orElse(null);
247
Jian Li07c27f32020-10-08 02:57:45 +0900248 PortNumber srcPortNum = context.inPacket().receivedFrom().port();
249 DeviceId srcDeviceId = context.inPacket().receivedFrom().deviceId();
250 boolean isEntryPort = false;
251
252 for (K8sNode node : k8sNodeService.completeNodes()) {
253 if (srcDeviceId.equals(node.intgBridge()) &&
254 srcPortNum.equals(node.intgEntryPortNum())) {
255 isEntryPort = true;
256 }
257 }
258
259 // if the ARP request is not initiated from regular k8s ports nor
260 // integration bridge entry port, we simply ignore the ARP request...
261 if (srcK8sPort == null && !isEntryPort) {
Jian Li4aa17642019-01-30 00:01:11 +0900262 log.warn("Failed to find source port(MAC:{})", ethPacket.getSourceMAC());
263 return;
264 }
265
Jian Li4aa17642019-01-30 00:01:11 +0900266 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
267
Jian Li07c27f32020-10-08 02:57:45 +0900268 // look up the MAC address from regular k8s ports
Jian Li4aa17642019-01-30 00:01:11 +0900269 MacAddress replyMac = k8sNetworkService.ports().stream()
Jian Li44c2b122019-05-03 14:46:34 +0900270 // .filter(p -> p.networkId().equals(srcPort.networkId()))
Jian Li4aa17642019-01-30 00:01:11 +0900271 .filter(p -> p.ipAddress().equals(targetIp))
272 .map(K8sPort::macAddress)
273 .findAny().orElse(null);
274
Jian Li07c27f32020-10-08 02:57:45 +0900275 // look up the MAC address from special integration entry port (e.g., LOCAL, k8s-int-os)
276 for (K8sNetwork network : k8sNetworkService.networks()) {
277 if (network.gatewayIp().equals(targetIp)) {
278 K8sNode node = k8sNodeService.node(network.name());
279 replyMac = node.intgEntryPortMac();
280 }
Jian Li4aa17642019-01-30 00:01:11 +0900281 }
282
283 if (replyMac == null) {
Jian Li140d8a22019-04-24 23:41:44 +0900284 String cidr = k8sNetworkService.networks().stream()
285 .map(K8sNetwork::cidr).findAny().orElse(null);
286
287 if (cidr != null) {
288 String unshiftedIp = unshiftIpDomain(targetIp.toString(),
289 SHIFTED_IP_PREFIX, cidr);
290
Jian Li004526d2019-02-25 16:26:27 +0900291 replyMac = k8sNetworkService.ports().stream()
Jian Li140d8a22019-04-24 23:41:44 +0900292 .filter(p -> p.ipAddress().equals(IpAddress.valueOf(unshiftedIp)))
Jian Li004526d2019-02-25 16:26:27 +0900293 .map(K8sPort::macAddress)
294 .findAny().orElse(null);
Jian Li004526d2019-02-25 16:26:27 +0900295 }
296 }
297
298 if (replyMac == null) {
Jian Li7d111d72019-04-12 13:58:44 +0900299 Set<String> serviceIps = k8sServiceService.services().stream()
300 .map(s -> s.getSpec().getClusterIP())
301 .collect(Collectors.toSet());
302 if (serviceIps.contains(targetIp.toString())) {
303 replyMac = MacAddress.valueOf(SERVICE_FAKE_MAC_STR);
304 }
305 }
306
307 if (replyMac == null) {
Jian Li140d8a22019-04-24 23:41:44 +0900308
Jian Li44c2b122019-05-03 14:46:34 +0900309 if (targetIp.toString().startsWith(NODE_IP_PREFIX)) {
310 String targetIpPrefix = targetIp.toString().split("\\.")[1];
311 String nodePrefix = NODE_IP_PREFIX + "." + targetIpPrefix;
Jian Li140d8a22019-04-24 23:41:44 +0900312
Jian Lif4818b02020-11-04 15:58:18 +0900313 String origNodeCidr = k8sNodeService.completeNodes().stream()
314 .map(n -> n.nodeIp().toString()).findAny().orElse(null);
Jian Li140d8a22019-04-24 23:41:44 +0900315
Jian Lif4818b02020-11-04 15:58:18 +0900316 if (origNodeCidr != null) {
317 String origNodeIp = unshiftIpDomain(targetIp.toString(),
318 nodePrefix, origNodeCidr);
319 IpPrefix k8sNodeIpCidr = IpPrefix.valueOf(IpAddress.valueOf(origNodeCidr), 24);
320 SubnetUtils k8sNodeSubnet = new SubnetUtils(k8sNodeIpCidr.toString());
321 String k8sNodeGateway = getGatewayIp(k8sNodeIpCidr.toString()).toString();
322 String seekIp = "";
Jian Li44c2b122019-05-03 14:46:34 +0900323
Jian Lif4818b02020-11-04 15:58:18 +0900324 if (!k8sNodeSubnet.getInfo().isInRange(origNodeIp)) {
325 replyMac = extHostMacStore.asJavaMap().get(IpAddress.valueOf(k8sNodeGateway));
326 seekIp = k8sNodeGateway;
327 } else {
328 replyMac = extHostMacStore.asJavaMap().get(IpAddress.valueOf(origNodeIp));
329 seekIp = origNodeIp;
Jian Li44c2b122019-05-03 14:46:34 +0900330 }
331
332 // if the source hosts are not in k8s cluster range,
333 // we need to manually learn their MAC addresses
334 if (replyMac == null) {
335 ConnectPoint cp = context.inPacket().receivedFrom();
336 K8sNode k8sNode = k8sNodeService.node(cp.deviceId());
337
338 if (k8sNode != null) {
Jian Lif4818b02020-11-04 15:58:18 +0900339 // we use fake IP and MAC address as a source to
340 // query destination MAC address
341 setArpRequest(MacAddress.valueOf(NODE_FAKE_MAC_STR).toBytes(),
342 IpAddress.valueOf(NODE_FAKE_IP_STR).toOctets(),
343 IpAddress.valueOf(seekIp).toOctets(),
Jian Li44c2b122019-05-03 14:46:34 +0900344 k8sNode);
345 context.block();
346 return;
347 }
348 }
349 }
Jian Li140d8a22019-04-24 23:41:44 +0900350 }
351 }
352
353 if (replyMac == null) {
Jian Li57263ea2020-10-11 02:45:16 +0900354 replyMac = MacAddress.valueOf(gatewayMac);
Jian Li4aa17642019-01-30 00:01:11 +0900355 }
356
357 Ethernet ethReply = ARP.buildArpReply(
358 targetIp.getIp4Address(),
359 replyMac,
360 ethPacket);
361
362 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
363 .setOutput(context.inPacket().receivedFrom().port())
364 .build();
365
366 packetService.emit(new DefaultOutboundPacket(
367 context.inPacket().receivedFrom().deviceId(),
368 treatment,
369 ByteBuffer.wrap(ethReply.serialize())));
Jian Li1b08d652019-05-02 17:28:09 +0900370
371 context.block();
Jian Li4aa17642019-01-30 00:01:11 +0900372 }
373
Jian Li44c2b122019-05-03 14:46:34 +0900374 private void processArpReply(PacketContext context, Ethernet ethPacket) {
375 ARP arpPacket = (ARP) ethPacket.getPayload();
Jian Li44c2b122019-05-03 14:46:34 +0900376
Jian Lif4818b02020-11-04 15:58:18 +0900377 IpAddress srcIp = IpAddress.valueOf(INET, arpPacket.getSenderProtocolAddress());
378 MacAddress srcMac = MacAddress.valueOf(arpPacket.getSenderHardwareAddress());
379 IpAddress dstIp = IpAddress.valueOf(INET, arpPacket.getTargetProtocolAddress());
Jian Li44c2b122019-05-03 14:46:34 +0900380
Jian Lif4818b02020-11-04 15:58:18 +0900381 if (dstIp.equals(IpAddress.valueOf(NODE_FAKE_IP_STR))) {
Jian Li44c2b122019-05-03 14:46:34 +0900382 // we only add the host IP - MAC map store once,
383 // mutable MAP scenario is not considered for now
384 if (!extHostMacStore.containsKey(srcIp)) {
385 extHostMacStore.put(srcIp, srcMac);
386 }
Jian Lif4818b02020-11-04 15:58:18 +0900387
388 K8sNode k8sNode = k8sNodeService.nodes().stream()
389 .filter(n -> n.nodeIp().equals(srcIp))
390 .findAny().orElse(null);
391
392 if (k8sNode == null) {
393 return;
394 } else {
395 if (k8sNode.nodeInfo().nodeMac() != null) {
396 return;
397 }
398 }
399
400 // we update node MAC address which will be referred in node port scenario
401 K8sNodeInfo nodeInfo = new K8sNodeInfo(k8sNode.nodeIp(), srcMac);
402 K8sNode updatedNode = k8sNode.updateNodeInfo(nodeInfo);
403 k8sNodeService.updateNode(updatedNode);
Jian Li44c2b122019-05-03 14:46:34 +0900404 }
405 }
406
407 private void setArpRequest(byte[] senderMac, byte[] senderIp,
408 byte[] targetIp, K8sNode k8sNode) {
409 Ethernet ethRequest = ARP.buildArpRequest(senderMac,
410 senderIp, targetIp, VlanId.NO_VID);
411
Jian Lif4818b02020-11-04 15:58:18 +0900412 // TODO: we need to find a way of sending out ARP request to learn
413 // MAC addresses in NORMAL mode
414 PortNumber k8sExtToOsPatchPort = k8sNode.portNumByName(k8sNode.extBridge(),
415 k8sNode.k8sExtToOsPatchPortName());
416 if (k8sExtToOsPatchPort == null) {
417 log.warn("Kubernetes external to OpenStack patch port is null");
418 return;
419 }
Jian Li44c2b122019-05-03 14:46:34 +0900420 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Jian Lif4818b02020-11-04 15:58:18 +0900421 .setOutput(k8sExtToOsPatchPort)
Jian Li44c2b122019-05-03 14:46:34 +0900422 .build();
423
424 packetService.emit(new DefaultOutboundPacket(
Jian Lif4818b02020-11-04 15:58:18 +0900425 k8sNode.extBridge(),
Jian Li44c2b122019-05-03 14:46:34 +0900426 treatment,
427 ByteBuffer.wrap(ethRequest.serialize())));
428 }
429
Jian Li4aa17642019-01-30 00:01:11 +0900430 private String getArpMode() {
431 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
432 return getPropertyValue(properties, ARP_MODE);
433 }
434
435 /**
436 * Extracts properties from the component configuration context.
437 *
438 * @param context the component context
439 */
440 private void readComponentConfiguration(ComponentContext context) {
441 Dictionary<?, ?> properties = context.getProperties();
442
443 String updatedMac = Tools.get(properties, GATEWAY_MAC);
444 gatewayMac = updatedMac != null ? updatedMac : GATEWAY_MAC_DEFAULT;
445 log.info("Configured. Gateway MAC is {}", gatewayMac);
446 }
447
448 /**
449 * An internal packet processor which processes ARP request, and results in
450 * packet-out ARP reply.
451 */
452 private class InternalPacketProcessor implements PacketProcessor {
453
454 @Override
455 public void process(PacketContext context) {
456 if (context.isHandled()) {
457 return;
458 }
459
460 Ethernet ethPacket = context.inPacket().parsed();
461 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
462 return;
463 }
464
465 eventExecutor.execute(() -> processPacketIn(context, ethPacket));
466 }
467 }
468
469 /**
470 * An internal kubernetes node listener which is used for listening kubernetes
471 * node activity. As long as a node is in complete state, we will install
472 * default ARP rule to handle ARP request.
473 */
474 private class InternalNodeEventListener implements K8sNodeListener {
475
476 private boolean isRelevantHelper() {
477 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
478 }
479
480 @Override
481 public void event(K8sNodeEvent event) {
482 K8sNode k8sNode = event.subject();
483 switch (event.type()) {
484 case K8S_NODE_COMPLETE:
485 eventExecutor.execute(() -> processNodeCompletion(k8sNode));
486 break;
487 case K8S_NODE_INCOMPLETE:
488 eventExecutor.execute(() -> processNodeIncompletion(k8sNode));
489 break;
490 default:
491 break;
492 }
493 }
494
495 private void processNodeCompletion(K8sNode node) {
496 if (!isRelevantHelper()) {
497 return;
498 }
499
500 setDefaultArpRule(node, true);
Jian Lif4818b02020-11-04 15:58:18 +0900501 learnK8sNodeMac(node);
Jian Li4aa17642019-01-30 00:01:11 +0900502 }
503
504 private void processNodeIncompletion(K8sNode node) {
505 if (!isRelevantHelper()) {
506 return;
507 }
508
509 setDefaultArpRule(node, false);
510 }
511
512 private void setDefaultArpRule(K8sNode node, boolean install) {
513
514 if (getArpMode() == null) {
515 return;
516 }
517
518 switch (getArpMode()) {
519 case ARP_PROXY_MODE:
520 setDefaultArpRuleForProxyMode(node, install);
521 break;
522 case ARP_BROADCAST_MODE:
523 // TODO: need to implement broadcast mode
524 log.warn("Not implemented yet.");
525 break;
526 default:
527 log.warn("Invalid ARP mode {}. Please use either " +
528 "broadcast or proxy mode.", getArpMode());
529 break;
530 }
531 }
532
533 private void setDefaultArpRuleForProxyMode(K8sNode node, boolean install) {
534 TrafficSelector selector = DefaultTrafficSelector.builder()
535 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
536 .build();
537
538 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
539 .punt()
540 .build();
541
542 k8sFlowRuleService.setRule(
543 appId,
544 node.intgBridge(),
545 selector,
546 treatment,
547 PRIORITY_ARP_CONTROL_RULE,
548 ARP_TABLE,
549 install
550 );
551 }
Jian Lif4818b02020-11-04 15:58:18 +0900552
553 private void learnK8sNodeMac(K8sNode k8sNode) {
554 // if we already have a learned MAC address, we skip learning process
555 if (k8sNode.nodeMac() != null) {
556 return;
557 }
558
559 setArpRequest(MacAddress.valueOf(NODE_FAKE_MAC_STR).toBytes(),
560 IpAddress.valueOf(NODE_FAKE_IP_STR).toOctets(),
561 k8sNode.nodeIp().toOctets(),
562 k8sNode);
563 }
Jian Li4aa17642019-01-30 00:01:11 +0900564 }
565}