blob: c95f8c4e9dd2f46af0a838bd8e7bc46e7a70efb3 [file] [log] [blame]
Jian Li556709c2021-02-03 17:54:28 +09001/*
2 * Copyright 2021-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.kubevirtnetworking.impl;
17
18import com.google.common.collect.Lists;
Jian Li858ccd72021-02-04 17:25:01 +090019import org.onlab.packet.ARP;
20import org.onlab.packet.EthType;
Jian Li556709c2021-02-03 17:54:28 +090021import org.onlab.packet.Ethernet;
22import org.onlab.packet.IPv4;
Jian Li858ccd72021-02-04 17:25:01 +090023import org.onlab.packet.Ip4Address;
Jian Li556709c2021-02-03 17:54:28 +090024import org.onlab.packet.IpAddress;
Jian Li858ccd72021-02-04 17:25:01 +090025import org.onlab.packet.IpPrefix;
Daniel Park2884b232021-03-04 18:58:47 +090026import org.onlab.packet.MacAddress;
Jian Li556709c2021-02-03 17:54:28 +090027import org.onlab.packet.TpPort;
28import org.onlab.packet.UDP;
29import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
35import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
Jian Lifc7e6cf2021-04-08 11:13:24 +090036import org.onosproject.kubevirtnetworking.api.KubevirtNetworkAdminService;
Jian Li556709c2021-02-03 17:54:28 +090037import org.onosproject.kubevirtnetworking.api.KubevirtNetworkEvent;
38import org.onosproject.kubevirtnetworking.api.KubevirtNetworkListener;
39import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
Daniel Parkbabde9c2021-03-09 13:37:42 +090040import org.onosproject.kubevirtnetworking.api.KubevirtPort;
41import org.onosproject.kubevirtnetworking.api.KubevirtPortEvent;
42import org.onosproject.kubevirtnetworking.api.KubevirtPortListener;
43import org.onosproject.kubevirtnetworking.api.KubevirtPortService;
Daniel Park2884b232021-03-04 18:58:47 +090044import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
45import org.onosproject.kubevirtnetworking.api.KubevirtRouterAdminService;
46import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
47import org.onosproject.kubevirtnetworking.api.KubevirtRouterListener;
Jian Li556709c2021-02-03 17:54:28 +090048import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
49import org.onosproject.kubevirtnode.api.KubevirtNode;
50import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
51import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
52import org.onosproject.kubevirtnode.api.KubevirtNodeService;
53import org.onosproject.net.Device;
54import org.onosproject.net.DeviceId;
55import org.onosproject.net.PortNumber;
56import org.onosproject.net.behaviour.BridgeConfig;
57import org.onosproject.net.behaviour.BridgeDescription;
58import org.onosproject.net.behaviour.BridgeName;
59import org.onosproject.net.behaviour.ControllerInfo;
60import org.onosproject.net.behaviour.DefaultBridgeDescription;
61import org.onosproject.net.behaviour.DefaultPatchDescription;
62import org.onosproject.net.behaviour.InterfaceConfig;
63import org.onosproject.net.behaviour.PatchDescription;
64import org.onosproject.net.device.DeviceAdminService;
Daniel Parka8968802021-02-25 09:14:22 +090065import org.onosproject.net.driver.DriverService;
Jian Li556709c2021-02-03 17:54:28 +090066import org.onosproject.net.flow.DefaultTrafficSelector;
67import org.onosproject.net.flow.DefaultTrafficTreatment;
68import org.onosproject.net.flow.TrafficSelector;
69import org.onosproject.net.flow.TrafficTreatment;
70import org.osgi.service.component.annotations.Activate;
71import org.osgi.service.component.annotations.Component;
72import org.osgi.service.component.annotations.Deactivate;
73import org.osgi.service.component.annotations.Reference;
74import org.osgi.service.component.annotations.ReferenceCardinality;
75import org.slf4j.Logger;
76
77import java.util.List;
78import java.util.Objects;
Daniel Park2884b232021-03-04 18:58:47 +090079import java.util.Set;
Jian Li556709c2021-02-03 17:54:28 +090080import java.util.concurrent.ExecutorService;
81
82import static java.lang.Thread.sleep;
83import static java.util.concurrent.Executors.newSingleThreadExecutor;
Jian Li858ccd72021-02-04 17:25:01 +090084import static org.onlab.packet.ICMP.CODE_ECHO_REQEUST;
85import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
86import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
Jian Li556709c2021-02-03 17:54:28 +090087import static org.onlab.util.Tools.groupedThreads;
Daniel Parkbabde9c2021-03-09 13:37:42 +090088import static org.onosproject.kubevirtnetworking.api.Constants.FORWARDING_TABLE;
Jian Lif89d9602021-04-27 19:05:49 +090089import static org.onosproject.kubevirtnetworking.api.Constants.GW_ENTRY_TABLE;
Jian Li556709c2021-02-03 17:54:28 +090090import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
Jian Li8f944d42021-03-23 00:43:29 +090091import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_DEFAULT_RULE;
Jian Li858ccd72021-02-04 17:25:01 +090092import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
Jian Li556709c2021-02-03 17:54:28 +090093import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_DHCP_RULE;
Jian Li858ccd72021-02-04 17:25:01 +090094import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_FORWARDING_RULE;
95import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ICMP_RULE;
Daniel Parkbabde9c2021-03-09 13:37:42 +090096import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_INTERNAL_ROUTING_RULE;
Jian Li8f944d42021-03-23 00:43:29 +090097import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_IP_EGRESS_RULE;
98import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_IP_INGRESS_RULE;
Daniel Parkf3136042021-03-10 07:49:11 +090099import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_TUNNEL_RULE;
Jian Li8f944d42021-03-23 00:43:29 +0900100import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ACL_EGRESS_TABLE;
101import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ACL_INGRESS_TABLE;
Jian Li858ccd72021-02-04 17:25:01 +0900102import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ARP_TABLE;
Jian Li556709c2021-02-03 17:54:28 +0900103import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_DHCP_TABLE;
104import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_FORWARDING_TABLE;
Jian Li858ccd72021-02-04 17:25:01 +0900105import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ICMP_TABLE;
Jian Li556709c2021-02-03 17:54:28 +0900106import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_INBOUND_TABLE;
Jian Li858ccd72021-02-04 17:25:01 +0900107import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_TO_TUNNEL_PREFIX;
Daniel Parkf3136042021-03-10 07:49:11 +0900108import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_DEFAULT_TABLE;
Jian Li858ccd72021-02-04 17:25:01 +0900109import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_TO_TENANT_PREFIX;
Daniel Park4cb120b2021-03-24 12:30:50 +0900110import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.FLAT;
111import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VLAN;
Daniel Park2884b232021-03-04 18:58:47 +0900112import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
Daniel Parkbabde9c2021-03-09 13:37:42 +0900113import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterForKubevirtNetwork;
114import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterForKubevirtPort;
Daniel Parkf3136042021-03-10 07:49:11 +0900115import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterMacAddress;
116import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.portNumber;
Jian Li94b6d162021-04-15 17:09:11 +0900117import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.resolveHostname;
Jian Li556709c2021-02-03 17:54:28 +0900118import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.segmentIdHex;
Daniel Parkf3136042021-03-10 07:49:11 +0900119import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.tunnelPort;
120import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.tunnelToTenantPort;
Jian Li858ccd72021-02-04 17:25:01 +0900121import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.NXM_NX_IP_TTL;
122import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.NXM_OF_ICMP_TYPE;
Daniel Parkf3136042021-03-10 07:49:11 +0900123import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildExtension;
Jian Li858ccd72021-02-04 17:25:01 +0900124import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildLoadExtension;
125import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
126import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
127import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
128import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildMoveIpSrcToDstExtension;
Jian Li556709c2021-02-03 17:54:28 +0900129import static org.onosproject.kubevirtnode.api.Constants.TUNNEL_BRIDGE;
Daniel Parkf3136042021-03-10 07:49:11 +0900130import static org.onosproject.kubevirtnode.api.Constants.TUNNEL_TO_INTEGRATION;
Daniel Parka8968802021-02-25 09:14:22 +0900131import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
132import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER;
Jian Li556709c2021-02-03 17:54:28 +0900133import static org.slf4j.LoggerFactory.getLogger;
134
135/**
136 * Handles kubevirt network events.
137 */
138@Component(immediate = true)
139public class KubevirtNetworkHandler {
140 protected final Logger log = getLogger(getClass());
141 private static final String DEFAULT_OF_PROTO = "tcp";
142 private static final int DEFAULT_OFPORT = 6653;
143 private static final int DPID_BEGIN = 3;
144 private static final long SLEEP_MS = 3000; // we wait 3s for init each node
Jian Li858ccd72021-02-04 17:25:01 +0900145 private static final int DEFAULT_TTL = 0xff;
Jian Li556709c2021-02-03 17:54:28 +0900146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
148 protected CoreService coreService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
151 protected ClusterService clusterService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
154 protected LeadershipService leadershipService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
157 protected DeviceAdminService deviceService;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
160 protected KubevirtApiConfigService apiConfigService;
161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
163 protected KubevirtNodeService nodeService;
164
165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lifc7e6cf2021-04-08 11:13:24 +0900166 protected KubevirtNetworkAdminService networkService;
Jian Li556709c2021-02-03 17:54:28 +0900167
168 @Reference(cardinality = ReferenceCardinality.MANDATORY)
169 protected KubevirtFlowRuleService flowService;
170
Daniel Parka8968802021-02-25 09:14:22 +0900171 @Reference(cardinality = ReferenceCardinality.MANDATORY)
172 protected DriverService driverService;
173
Daniel Park2884b232021-03-04 18:58:47 +0900174 @Reference(cardinality = ReferenceCardinality.MANDATORY)
175 protected KubevirtRouterAdminService kubevirtRouterService;
176
Daniel Parkbabde9c2021-03-09 13:37:42 +0900177 @Reference(cardinality = ReferenceCardinality.MANDATORY)
178 protected KubevirtPortService kubevirtPortService;
179
180 @Reference(cardinality = ReferenceCardinality.MANDATORY)
181 protected KubevirtNetworkService kubevirtNetworkService;
182
183 @Reference(cardinality = ReferenceCardinality.MANDATORY)
184 protected KubevirtNodeService kubevirtNodeService;
185
Jian Li556709c2021-02-03 17:54:28 +0900186 private final KubevirtNetworkListener networkListener = new InternalNetworkEventListener();
187 private final KubevirtNodeListener nodeListener = new InternalNodeEventListener();
Daniel Parkbabde9c2021-03-09 13:37:42 +0900188 private final KubevirtPortListener portListener = new InternalKubevirtPortListener();
Jian Li556709c2021-02-03 17:54:28 +0900189
Daniel Park2884b232021-03-04 18:58:47 +0900190 private final InternalRouterEventListener kubevirtRouterlistener =
191 new InternalRouterEventListener();
192
Jian Li556709c2021-02-03 17:54:28 +0900193 private final ExecutorService eventExecutor = newSingleThreadExecutor(
194 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
195
196 private ApplicationId appId;
197 private NodeId localNodeId;
198
199 @Activate
200 protected void activate() {
201 appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
202 localNodeId = clusterService.getLocalNode().id();
Jian Li556709c2021-02-03 17:54:28 +0900203 leadershipService.runForLeadership(appId.name());
204
Daniel Parkbabde9c2021-03-09 13:37:42 +0900205 networkService.addListener(networkListener);
206 nodeService.addListener(nodeListener);
207 kubevirtPortService.addListener(portListener);
Daniel Park2884b232021-03-04 18:58:47 +0900208 kubevirtRouterService.addListener(kubevirtRouterlistener);
209
Jian Li556709c2021-02-03 17:54:28 +0900210 log.info("Started");
211 }
212
213 @Deactivate
214 protected void deactivate() {
215 networkService.removeListener(networkListener);
216 nodeService.removeListener(nodeListener);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900217 kubevirtPortService.removeListener(portListener);
Daniel Park2884b232021-03-04 18:58:47 +0900218 kubevirtRouterService.removeListener(kubevirtRouterlistener);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900219 leadershipService.withdraw(appId.name());
Jian Li556709c2021-02-03 17:54:28 +0900220 eventExecutor.shutdown();
221
222 log.info("Stopped");
223 }
224
225 private void createBridge(KubevirtNode node, KubevirtNetwork network) {
226
Jian Li8f944d42021-03-23 00:43:29 +0900227 Device tenantBridge = deviceService.getDevice(network.tenantDeviceId(node.hostname()));
228 if (tenantBridge != null) {
229 log.warn("The tenant bridge {} already exists at node {}",
Jian Li556709c2021-02-03 17:54:28 +0900230 network.tenantBridgeName(), node.hostname());
Daniel Parkf3136042021-03-10 07:49:11 +0900231 setDefaultRulesForTenantNetwork(node, network);
Jian Li556709c2021-02-03 17:54:28 +0900232 return;
233 }
234
235 Device device = deviceService.getDevice(node.ovsdb());
236
Jian Li94b6d162021-04-15 17:09:11 +0900237 IpAddress serverIp;
238 String serviceFqdn = apiConfigService.apiConfig().serviceFqdn();
239 IpAddress serviceIp = null;
240
241 if (serviceFqdn != null) {
242 serviceIp = resolveHostname(serviceFqdn);
243 }
244
245 if (serviceIp != null) {
246 serverIp = serviceIp;
247 } else {
248 serverIp = apiConfigService.apiConfig().ipAddress();
249 }
250
Jian Li556709c2021-02-03 17:54:28 +0900251 ControllerInfo controlInfo =
252 new ControllerInfo(serverIp, DEFAULT_OFPORT, DEFAULT_OF_PROTO);
253 List<ControllerInfo> controllers = Lists.newArrayList(controlInfo);
254
255 String dpid = network.tenantDeviceId(
256 node.hostname()).toString().substring(DPID_BEGIN);
257
258 BridgeDescription.Builder builder = DefaultBridgeDescription.builder()
259 .name(network.tenantBridgeName())
260 .failMode(BridgeDescription.FailMode.SECURE)
261 .datapathId(dpid)
262 .disableInBand()
263 .controllers(controllers);
264
265 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
266 bridgeConfig.addBridge(builder.build());
267 }
268
269 private void removeBridge(KubevirtNode node, KubevirtNetwork network) {
270 Device device = deviceService.getDevice(node.ovsdb());
271
272 BridgeName bridgeName = BridgeName.bridgeName(network.tenantBridgeName());
273
274 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
275 bridgeConfig.deleteBridge(bridgeName);
276 deviceService.removeDevice(network.tenantDeviceId(node.hostname()));
277 }
278
Daniel Parkf3136042021-03-10 07:49:11 +0900279 private void createPatchTenantInterface(KubevirtNode node, KubevirtNetwork network) {
Jian Li556709c2021-02-03 17:54:28 +0900280 Device device = deviceService.getDevice(node.ovsdb());
281
282 if (device == null || !device.is(InterfaceConfig.class)) {
283 log.error("Failed to create patch interface on {}", node.ovsdb());
284 return;
285 }
286
287 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
288
Jian Li858ccd72021-02-04 17:25:01 +0900289 String tenantToTunIntf =
290 TENANT_TO_TUNNEL_PREFIX + segmentIdHex(network.segmentId());
291 String tunToTenantIntf =
292 TUNNEL_TO_TENANT_PREFIX + segmentIdHex(network.segmentId());
Jian Li556709c2021-02-03 17:54:28 +0900293
Jian Li858ccd72021-02-04 17:25:01 +0900294 // tenant bridge -> tunnel bridge
295 PatchDescription brTenantTunPatchDesc =
Jian Li556709c2021-02-03 17:54:28 +0900296 DefaultPatchDescription.builder()
297 .deviceId(network.tenantBridgeName())
Jian Li858ccd72021-02-04 17:25:01 +0900298 .ifaceName(tenantToTunIntf)
299 .peer(tunToTenantIntf)
Jian Li556709c2021-02-03 17:54:28 +0900300 .build();
301
Jian Li858ccd72021-02-04 17:25:01 +0900302 ifaceConfig.addPatchMode(tenantToTunIntf, brTenantTunPatchDesc);
Jian Li556709c2021-02-03 17:54:28 +0900303
Jian Li858ccd72021-02-04 17:25:01 +0900304 // tunnel bridge -> tenant bridge
305 PatchDescription brTunTenantPatchDesc =
Jian Li556709c2021-02-03 17:54:28 +0900306 DefaultPatchDescription.builder()
307 .deviceId(TUNNEL_BRIDGE)
Jian Li858ccd72021-02-04 17:25:01 +0900308 .ifaceName(tunToTenantIntf)
309 .peer(tenantToTunIntf)
Jian Li556709c2021-02-03 17:54:28 +0900310 .build();
Jian Li858ccd72021-02-04 17:25:01 +0900311 ifaceConfig.addPatchMode(tunToTenantIntf, brTunTenantPatchDesc);
Jian Li556709c2021-02-03 17:54:28 +0900312 }
313
314 private void removePatchInterface(KubevirtNode node, KubevirtNetwork network) {
315 Device device = deviceService.getDevice(node.ovsdb());
316
317 if (device == null || !device.is(InterfaceConfig.class)) {
318 log.error("Failed to create patch interface on {}", node.ovsdb());
319 return;
320 }
321
322 InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
323
Jian Li858ccd72021-02-04 17:25:01 +0900324 String tunToIntIntf = TUNNEL_TO_TENANT_PREFIX + segmentIdHex(network.segmentId());
Jian Li556709c2021-02-03 17:54:28 +0900325
326 ifaceConfig.removePatchMode(tunToIntIntf);
327 }
328
Jian Li8f944d42021-03-23 00:43:29 +0900329 private void setGatewayArpRulesForTenantNetwork(KubevirtNode node,
330 KubevirtNetwork network) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900331
332 KubevirtRouter router = getRouterForKubevirtNetwork(kubevirtRouterService, network);
333 if (router == null) {
334 return;
335 }
336
337 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
338 if (electedGw == null) {
339 return;
340 }
341
Jian Li8f944d42021-03-23 00:43:29 +0900342 setGatewayArpRuleForTenantInternalNetwork(router, network, TENANT_ARP_TABLE,
343 electedGw.intgBridge(), network.tenantDeviceId(node.hostname()), true);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900344 }
345
Jian Li8f944d42021-03-23 00:43:29 +0900346 private void setGatewayIcmpRulesForTenantNetwork(KubevirtNode node,
347 KubevirtNetwork network) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900348 KubevirtRouter router = getRouterForKubevirtNetwork(kubevirtRouterService, network);
349 if (router == null) {
350 return;
351 }
352
353 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
354 if (electedGw == null) {
355 return;
356 }
357
Jian Li8f944d42021-03-23 00:43:29 +0900358 setGatewayIcmpRuleForTenantInternalNetwork(router, network, TENANT_ICMP_TABLE,
359 electedGw.intgBridge(), network.tenantDeviceId(node.hostname()), true);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900360 }
361
Jian Li8f944d42021-03-23 00:43:29 +0900362 private void setGatewayRuleToWorkerNodeWhenNodeCreated(KubevirtNode node,
363 KubevirtNetwork network) {
Daniel Parkf3136042021-03-10 07:49:11 +0900364 KubevirtRouter router = getRouterForKubevirtNetwork(kubevirtRouterService, network);
365 if (router == null) {
366 return;
367 }
Daniel Parkbabde9c2021-03-09 13:37:42 +0900368
Daniel Parkf3136042021-03-10 07:49:11 +0900369 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
370 if (electedGw == null) {
371 return;
372 }
373
Jian Li8f944d42021-03-23 00:43:29 +0900374 setDefaultGatewayRuleToWorkerNodeTunBridge(router, network,
375 electedGw.intgBridge(), node, true);
Daniel Parkf3136042021-03-10 07:49:11 +0900376 }
377
Jian Li8f944d42021-03-23 00:43:29 +0900378 private void setDefaultRulesForTenantNetwork(KubevirtNode node,
379 KubevirtNetwork network) {
Jian Li556709c2021-02-03 17:54:28 +0900380 DeviceId deviceId = network.tenantDeviceId(node.hostname());
381
382 while (!deviceService.isAvailable(deviceId)) {
383 log.warn("Device {} is not ready for installing rules", deviceId);
384
385 try {
386 sleep(SLEEP_MS);
387 } catch (InterruptedException e) {
388 log.error("Failed to check device availability", e);
389 }
390 }
391
392 flowService.connectTables(deviceId, TENANT_INBOUND_TABLE, TENANT_DHCP_TABLE);
Jian Li858ccd72021-02-04 17:25:01 +0900393 flowService.connectTables(deviceId, TENANT_DHCP_TABLE, TENANT_ARP_TABLE);
394 flowService.connectTables(deviceId, TENANT_ARP_TABLE, TENANT_ICMP_TABLE);
395 flowService.connectTables(deviceId, TENANT_ICMP_TABLE, TENANT_FORWARDING_TABLE);
Jian Li556709c2021-02-03 17:54:28 +0900396
Jian Li8f944d42021-03-23 00:43:29 +0900397 setArpRuleForTenantNetwork(deviceId, true);
Daniel Parkf3136042021-03-10 07:49:11 +0900398 setDhcpRuleForTenantNetwork(deviceId, true);
Jian Li556709c2021-02-03 17:54:28 +0900399 setForwardingRule(deviceId, true);
400
Jian Li8f944d42021-03-23 00:43:29 +0900401 // security group related rules
402 setTenantIngressTransitionRule(network, network.tenantDeviceId(node.hostname()), true);
Jian Lif89d9602021-04-27 19:05:49 +0900403 setTenantEgressTransitionRule(network.tenantDeviceId(node.hostname()), true);
Jian Li8f944d42021-03-23 00:43:29 +0900404
Jian Li556709c2021-02-03 17:54:28 +0900405 log.info("Install default flow rules for tenant bridge {}", network.tenantBridgeName());
406 }
407
Daniel Parkf3136042021-03-10 07:49:11 +0900408 private void setDhcpRuleForTenantNetwork(DeviceId deviceId, boolean install) {
Jian Li556709c2021-02-03 17:54:28 +0900409 TrafficSelector selector = DefaultTrafficSelector.builder()
410 .matchEthType(Ethernet.TYPE_IPV4)
411 .matchIPProtocol(IPv4.PROTOCOL_UDP)
412 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
413 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
414 .build();
415
416 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
417 .punt()
418 .build();
419
420 flowService.setRule(
421 appId,
422 deviceId,
423 selector,
424 treatment,
425 PRIORITY_DHCP_RULE,
426 TENANT_DHCP_TABLE,
427 install);
428 }
429
Jian Li858ccd72021-02-04 17:25:01 +0900430 private void setForwardingRule(DeviceId deviceId, boolean install) {
Jian Li556709c2021-02-03 17:54:28 +0900431 TrafficSelector selector = DefaultTrafficSelector.builder().build();
432 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
433 .setOutput(PortNumber.NORMAL)
434 .build();
435
436 flowService.setRule(
437 appId,
438 deviceId,
439 selector,
440 treatment,
Jian Li858ccd72021-02-04 17:25:01 +0900441 PRIORITY_FORWARDING_RULE,
Jian Li556709c2021-02-03 17:54:28 +0900442 TENANT_FORWARDING_TABLE,
443 install);
444 }
445
Daniel Park2884b232021-03-04 18:58:47 +0900446 private void initGatewayNodeForInternalNetwork(KubevirtNetwork network,
447 KubevirtRouter router,
448 KubevirtNode electedGateway,
449 boolean install) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900450 switch (network.type()) {
451 case VXLAN:
452 case GRE:
453 case GENEVE:
Jian Li8f944d42021-03-23 00:43:29 +0900454 setDefaultEgressRuleToGatewayNode(router, network,
455 electedGateway.intgBridge(), install);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900456 kubevirtNodeService.completeNodes(WORKER).forEach(node -> {
Jian Li8f944d42021-03-23 00:43:29 +0900457 setGatewayArpRuleForTenantInternalNetwork(router, network,
458 TENANT_ARP_TABLE, electedGateway.intgBridge(),
Daniel Parkbabde9c2021-03-09 13:37:42 +0900459 network.tenantDeviceId(node.hostname()), install);
Jian Li8f944d42021-03-23 00:43:29 +0900460 setGatewayIcmpRuleForTenantInternalNetwork(router, network,
461 TENANT_ICMP_TABLE, electedGateway.intgBridge(),
Daniel Parkbabde9c2021-03-09 13:37:42 +0900462 network.tenantDeviceId(node.hostname()), install);
Jian Li517597a2021-03-22 11:04:52 +0900463 setDefaultGatewayRuleToWorkerNodeTunBridge(router, network,
Daniel Parkf3136042021-03-10 07:49:11 +0900464 electedGateway.intgBridge(), node, install);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900465 });
Daniel Park4cb120b2021-03-24 12:30:50 +0900466 setGatewayProviderInterNetworkRoutingWithinSameRouter(network, router, electedGateway, install);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900467 break;
468 case FLAT:
469 case VLAN:
Jian Li8f944d42021-03-23 00:43:29 +0900470 setGatewayArpRuleForProviderInternalNetwork(router, network,
Jian Lif89d9602021-04-27 19:05:49 +0900471 GW_ENTRY_TABLE, electedGateway.intgBridge(), install);
Jian Li8f944d42021-03-23 00:43:29 +0900472 setGatewayIcmpRuleForProviderInternalNetwork(router, network,
Jian Lif89d9602021-04-27 19:05:49 +0900473 GW_ENTRY_TABLE, electedGateway.intgBridge(), install);
Jian Li8f944d42021-03-23 00:43:29 +0900474 setGatewayProviderInterNetworkRoutingWithinSameRouter(network,
475 router, electedGateway, install);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900476 break;
477 default:
478 // do nothing
479 break;
480 }
Daniel Park2884b232021-03-04 18:58:47 +0900481 }
482
Jian Li517597a2021-03-22 11:04:52 +0900483 private void setDefaultGatewayRuleToWorkerNodeTunBridge(KubevirtRouter router,
484 KubevirtNetwork network,
485 DeviceId gwDeviceId,
486 KubevirtNode workerNode,
487 boolean install) {
Daniel Parkf3136042021-03-10 07:49:11 +0900488 MacAddress routerMacAddress = getRouterMacAddress(router);
Daniel Park2884b232021-03-04 18:58:47 +0900489
Daniel Parkf3136042021-03-10 07:49:11 +0900490 if (routerMacAddress == null) {
491 log.warn("Setting gateway default eggress rule to gateway for tenant internal network because " +
492 "there's no br-int port for device {}", gwDeviceId);
493 return;
494 }
495
496 KubevirtNode gwNode = kubevirtNodeService.node(gwDeviceId);
497
498 if (gwNode == null) {
499 log.warn("Setting gateway default eggress rule to gateway for tenant internal network because " +
500 "there's no gateway node for device {}", gwDeviceId);
501 return;
502 }
503
504
505 PortNumber patchPortNumber = tunnelToTenantPort(workerNode, network);
506 if (patchPortNumber == null) {
507 return;
508 }
509
510 PortNumber tunnelPortNumber = tunnelPort(workerNode, network);
511 if (tunnelPortNumber == null) {
512 return;
513 }
514
515 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
516 .matchInPort(patchPortNumber)
517 .matchEthType(Ethernet.TYPE_IPV4)
518 .matchEthDst((routerMacAddress));
519
520 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
521 .setTunnelId(Long.parseLong(network.segmentId()))
522 .extension(buildExtension(
523 deviceService,
524 workerNode.tunBridge(),
525 gwNode.dataIp().getIp4Address()),
526 workerNode.tunBridge())
527 .setOutput(tunnelPortNumber);
528
529 flowService.setRule(
530 appId,
531 workerNode.tunBridge(),
532 sBuilder.build(),
533 tBuilder.build(),
534 PRIORITY_FORWARDING_RULE,
535 TUNNEL_DEFAULT_TABLE,
536 install);
537 }
538
Jian Li8f944d42021-03-23 00:43:29 +0900539 private void setTenantIngressTransitionRule(KubevirtNetwork network,
540 DeviceId deviceId, boolean install) {
541 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
542 sBuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort())
543 .matchInPort(network.tenantToTunnelPort(deviceId));
544
545 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
546 tBuilder.transition(TENANT_ACL_INGRESS_TABLE);
547
548 flowService.setRule(appId,
549 deviceId,
550 sBuilder.build(),
551 tBuilder.build(),
552 PRIORITY_IP_INGRESS_RULE,
553 TENANT_ICMP_TABLE,
554 install
555 );
556 }
557
Jian Lif89d9602021-04-27 19:05:49 +0900558 private void setTenantEgressTransitionRule(DeviceId deviceId, boolean install) {
Jian Li8f944d42021-03-23 00:43:29 +0900559 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
560 sBuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
561
562 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
563 tBuilder.transition(TENANT_ACL_EGRESS_TABLE);
564
565 flowService.setRule(appId,
566 deviceId,
567 sBuilder.build(),
568 tBuilder.build(),
569 PRIORITY_IP_EGRESS_RULE,
570 TENANT_ICMP_TABLE,
571 install
572 );
573 }
574
Daniel Parkf3136042021-03-10 07:49:11 +0900575 private void setDefaultEgressRuleToGatewayNode(KubevirtRouter router,
576 KubevirtNetwork network,
577 DeviceId gwDeviceId,
578 boolean install) {
579 MacAddress routerMacAddress = getRouterMacAddress(router);
580
581 if (routerMacAddress == null) {
582 log.warn("Setting gateway default eggress rule to gateway for tenant internal network because " +
583 "there's no br-int port for device {}", gwDeviceId);
584 return;
585 }
586
587 KubevirtNode gwNode = kubevirtNodeService.node(gwDeviceId);
588
589 if (gwNode == null) {
590 log.warn("Setting gateway default eggress rule to gateway for tenant internal network because " +
591 "there's no gateway node for device {}", gwDeviceId);
592 return;
593 }
594
595 PortNumber tunToIntPortNum = portNumber(gwNode.tunBridge(), TUNNEL_TO_INTEGRATION);
596
597 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
598 .matchTunnelId(Long.parseLong(network.segmentId()));
599
600 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
601 .setOutput(tunToIntPortNum);
602
603 flowService.setRule(
604 appId,
605 gwNode.tunBridge(),
606 sBuilder.build(),
607 tBuilder.build(),
608 PRIORITY_TUNNEL_RULE,
609 TUNNEL_DEFAULT_TABLE,
610 install);
611 }
612
613
614 private void setGatewayIcmpRuleForTenantInternalNetwork(KubevirtRouter router,
615 KubevirtNetwork network,
616 int tableNum,
617 DeviceId gwDeviceId,
618 DeviceId tenantDeviceId,
619 boolean install) {
620 MacAddress routerMacAddress = getRouterMacAddress(router);
621
622 if (routerMacAddress == null) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900623 log.warn("Setting gateway ICMP rule for internal network because " +
624 "there's no br-int port for device {}", gwDeviceId);
625 return;
626 }
627
628 Device device = deviceService.getDevice(tenantDeviceId);
629
630 if (device == null) {
631 log.warn("Setting gateway icmp rule for internal network because " +
632 "there's no tenant device for {} to install gateway arp rule",
633 tenantDeviceId);
634 return;
635 }
636
637
638 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
639 .matchEthType(Ethernet.TYPE_IPV4)
640 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
641 .matchIcmpType(TYPE_ECHO_REQUEST)
642 .matchIcmpCode(CODE_ECHO_REQEUST)
643 .matchIPDst(IpPrefix.valueOf(network.gatewayIp(), 32));
644
645 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
646 .extension(buildMoveEthSrcToDstExtension(device), device.id())
647 .extension(buildMoveIpSrcToDstExtension(device), device.id())
648 .extension(buildLoadExtension(device,
649 NXM_NX_IP_TTL, DEFAULT_TTL), device.id())
650 .extension(buildLoadExtension(device,
651 NXM_OF_ICMP_TYPE, TYPE_ECHO_REPLY), device.id())
652 .setIpSrc(network.gatewayIp())
Daniel Parkf3136042021-03-10 07:49:11 +0900653 .setEthSrc(routerMacAddress)
Daniel Parkbabde9c2021-03-09 13:37:42 +0900654 .setOutput(PortNumber.IN_PORT);
655
656 flowService.setRule(
657 appId,
658 tenantDeviceId,
659 sBuilder.build(),
660 tBuilder.build(),
661 PRIORITY_ICMP_RULE,
662 tableNum,
663 install);
664 }
665
Jian Li8f944d42021-03-23 00:43:29 +0900666 private void setArpRuleForTenantNetwork(DeviceId tenantDeviceId,
667 boolean install) {
668 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
669 .matchEthType(EthType.EtherType.ARP.ethType().toShort());
670
671 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
672 .transition(TENANT_FORWARDING_TABLE);
673
674 flowService.setRule(
675 appId,
676 tenantDeviceId,
677 sBuilder.build(),
678 tBuilder.build(),
679 PRIORITY_ARP_DEFAULT_RULE,
680 TENANT_ARP_TABLE,
681 install
682 );
683 }
684
Daniel Parkf3136042021-03-10 07:49:11 +0900685 private void setGatewayArpRuleForTenantInternalNetwork(KubevirtRouter router,
686 KubevirtNetwork network,
687 int tableNum,
688 DeviceId gwDeviceId,
689 DeviceId tenantDeviceId,
690 boolean install) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900691
Daniel Parkf3136042021-03-10 07:49:11 +0900692 MacAddress routerMacAddress = getRouterMacAddress(router);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900693
Daniel Parkf3136042021-03-10 07:49:11 +0900694 if (routerMacAddress == null) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900695 log.warn("Setting gateway arp rule for internal network because " +
696 "there's no br-int port for device {}", gwDeviceId);
697 return;
698 }
699
700 Device device = deviceService.getDevice(tenantDeviceId);
701
702 if (device == null) {
703 log.warn("Setting gateway arp rule for internal network because " +
704 "there's no tenant device for {} to install gateway arp rule",
705 tenantDeviceId);
706 return;
707 }
708
709
710 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
711 sBuilder.matchEthType(EthType.EtherType.ARP.ethType().toShort())
712 .matchArpOp(ARP.OP_REQUEST)
713 .matchArpTpa(Ip4Address.valueOf(network.gatewayIp().toString()));
714
715 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
716 tBuilder.extension(buildMoveEthSrcToDstExtension(device), device.id())
717 .extension(buildMoveArpShaToThaExtension(device), device.id())
718 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
719 .setArpOp(ARP.OP_REPLY)
Daniel Parkf3136042021-03-10 07:49:11 +0900720 .setArpSha(routerMacAddress)
Daniel Parkbabde9c2021-03-09 13:37:42 +0900721 .setArpSpa(Ip4Address.valueOf(network.gatewayIp().toString()))
Daniel Parkf3136042021-03-10 07:49:11 +0900722 .setEthSrc(routerMacAddress)
Daniel Parkbabde9c2021-03-09 13:37:42 +0900723 .setOutput(PortNumber.IN_PORT);
724
725 flowService.setRule(
726 appId,
727 device.id(),
728 sBuilder.build(),
729 tBuilder.build(),
730 PRIORITY_ARP_GATEWAY_RULE,
731 tableNum,
732 install
733 );
734 }
735
Daniel Parkf3136042021-03-10 07:49:11 +0900736 private void setGatewayProviderInterNetworkRoutingWithinSameRouter(
Daniel Parkbabde9c2021-03-09 13:37:42 +0900737 KubevirtNetwork network, KubevirtRouter router, KubevirtNode gatewayNode, boolean install) {
Daniel Park2884b232021-03-04 18:58:47 +0900738 router.internal().forEach(srcNetwork -> {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900739 if (srcNetwork.equals(network.networkId())
740 || kubevirtNetworkService.network(srcNetwork) == null) {
Daniel Park2884b232021-03-04 18:58:47 +0900741 return;
742 }
743
Daniel Parkbabde9c2021-03-09 13:37:42 +0900744 kubevirtPortService.ports(network.networkId()).forEach(port -> {
Daniel Parkf3136042021-03-10 07:49:11 +0900745 setGatewayInterNetworkRoutingFromNetworkToPort(router, kubevirtNetworkService.network(srcNetwork),
Daniel Parkbabde9c2021-03-09 13:37:42 +0900746 port, gatewayNode, install);
747 });
Daniel Park2884b232021-03-04 18:58:47 +0900748 });
749 }
750
Daniel Parkf3136042021-03-10 07:49:11 +0900751 private void setGatewayInterNetworkRoutingFromNetworkToPort(KubevirtRouter router,
752 KubevirtNetwork srcNetwork,
753 KubevirtPort dstPort,
754 KubevirtNode gatewayNode,
755 boolean install) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900756 Device gwDevice = deviceService.getDevice(gatewayNode.intgBridge());
757
758 if (gwDevice == null) {
759 log.warn("Failed to set internal network routing rule because " +
760 "there's no device Id for device {}", gatewayNode.intgBridge());
761 return;
762 }
763
Daniel Parkf3136042021-03-10 07:49:11 +0900764 MacAddress routerMacAddress = getRouterMacAddress(router);
Daniel Park2884b232021-03-04 18:58:47 +0900765
Daniel Parkf3136042021-03-10 07:49:11 +0900766 if (routerMacAddress == null) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900767 log.warn("Failed to set internal network routing rule because " +
768 "there's no br-int port for device {}", gatewayNode.intgBridge());
769 return;
770 }
771
Daniel Park4cb120b2021-03-24 12:30:50 +0900772 TrafficSelector.Builder sBuilder;
773 TrafficTreatment treatment;
Daniel Parkbabde9c2021-03-09 13:37:42 +0900774
Daniel Park4cb120b2021-03-24 12:30:50 +0900775 if (srcNetwork.type() == FLAT || srcNetwork.type() == VLAN) {
776 sBuilder = DefaultTrafficSelector.builder()
777 .matchEthType(Ethernet.TYPE_IPV4)
778 .matchEthDst(routerMacAddress)
779 .matchIPSrc(IpPrefix.valueOf(srcNetwork.cidr()))
780 .matchIPDst(IpPrefix.valueOf(dstPort.ipAddress(), 32));
Daniel Parkbabde9c2021-03-09 13:37:42 +0900781
Daniel Park4cb120b2021-03-24 12:30:50 +0900782 treatment = DefaultTrafficTreatment.builder()
783 .setEthSrc(routerMacAddress)
784 .setEthDst(dstPort.macAddress())
785 .transition(FORWARDING_TABLE)
786 .build();
787
788 flowService.setRule(
789 appId,
790 gwDevice.id(),
791 sBuilder.build(),
792 treatment,
793 PRIORITY_INTERNAL_ROUTING_RULE,
Jian Lif89d9602021-04-27 19:05:49 +0900794 GW_ENTRY_TABLE,
Daniel Park4cb120b2021-03-24 12:30:50 +0900795 install);
796 } else {
797 KubevirtNetwork dstNetwork = kubevirtNetworkService.network(dstPort.networkId());
798 if (dstNetwork == null) {
799 return;
800 }
801
802 KubevirtNode dstPortWorkerNode = kubevirtNodeService.node(dstPort.deviceId());
803 if (dstPortWorkerNode == null) {
804 return;
805 }
806
807 sBuilder = DefaultTrafficSelector.builder()
808 .matchEthType(Ethernet.TYPE_IPV4)
809 .matchEthDst(routerMacAddress)
810 .matchTunnelId(Long.parseLong(srcNetwork.segmentId()))
811 .matchIPSrc(IpPrefix.valueOf(srcNetwork.cidr()))
812 .matchIPDst(IpPrefix.valueOf(dstPort.ipAddress(), 32));
813
814 treatment = DefaultTrafficTreatment.builder()
815 .setTunnelId(Long.parseLong(dstNetwork.segmentId()))
816 .setEthSrc(routerMacAddress)
817 .setEthDst(dstPort.macAddress())
818 .extension(buildExtension(
819 deviceService,
820 gatewayNode.tunBridge(),
821 dstPortWorkerNode.dataIp().getIp4Address()),
822 gatewayNode.tunBridge())
823 .setOutput(PortNumber.IN_PORT)
824 .build();
825
826 flowService.setRule(
827 appId,
828 gatewayNode.tunBridge(),
829 sBuilder.build(),
830 treatment,
831 PRIORITY_INTERNAL_ROUTING_RULE,
832 TUNNEL_DEFAULT_TABLE,
833 install);
834 }
Daniel Parkbabde9c2021-03-09 13:37:42 +0900835 }
836
Daniel Parkf3136042021-03-10 07:49:11 +0900837 private void setGatewayArpRuleForProviderInternalNetwork(KubevirtRouter router, KubevirtNetwork network,
838 int tableNum, DeviceId gwDeviceId, boolean install) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900839
840
841 Device device = deviceService.getDevice(gwDeviceId);
Daniel Parkf3136042021-03-10 07:49:11 +0900842 MacAddress routerMacAddress = getRouterMacAddress(router);
Daniel Parkbabde9c2021-03-09 13:37:42 +0900843
Daniel Parkf3136042021-03-10 07:49:11 +0900844 if (routerMacAddress == null) {
Daniel Parkbabde9c2021-03-09 13:37:42 +0900845 log.warn("Setting gateway arp rule for internal network because " +
846 "there's no br-int port for device {}", gwDeviceId);
Daniel Park2884b232021-03-04 18:58:47 +0900847 return;
848 }
849
Jian Li858ccd72021-02-04 17:25:01 +0900850 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
851 sBuilder.matchEthType(EthType.EtherType.ARP.ethType().toShort())
852 .matchArpOp(ARP.OP_REQUEST)
853 .matchArpTpa(Ip4Address.valueOf(network.gatewayIp().toString()));
854
855 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
856 tBuilder.extension(buildMoveEthSrcToDstExtension(device), device.id())
857 .extension(buildMoveArpShaToThaExtension(device), device.id())
858 .extension(buildMoveArpSpaToTpaExtension(device), device.id())
859 .setArpOp(ARP.OP_REPLY)
Daniel Parkf3136042021-03-10 07:49:11 +0900860 .setArpSha(routerMacAddress)
Jian Li858ccd72021-02-04 17:25:01 +0900861 .setArpSpa(Ip4Address.valueOf(network.gatewayIp().toString()))
Daniel Parkf3136042021-03-10 07:49:11 +0900862 .setEthSrc(routerMacAddress)
Jian Li858ccd72021-02-04 17:25:01 +0900863 .setOutput(PortNumber.IN_PORT);
864
865 flowService.setRule(
866 appId,
867 device.id(),
868 sBuilder.build(),
869 tBuilder.build(),
870 PRIORITY_ARP_GATEWAY_RULE,
Daniel Parka8968802021-02-25 09:14:22 +0900871 tableNum,
Jian Li858ccd72021-02-04 17:25:01 +0900872 install
873 );
874 }
875
Daniel Park2884b232021-03-04 18:58:47 +0900876 /**
877 * Sends ICMP echo reply for the ICMP echo request from the kubevirt VM.
878 *
Daniel Parkf3136042021-03-10 07:49:11 +0900879 * @param router kubevirt router
Daniel Park2884b232021-03-04 18:58:47 +0900880 * @param network kubevirt network
881 * @param tableNum flow table number
882 * @param deviceId device id of the selected gateway for the network
883 * @param install install if true, remove otherwise
884 */
Daniel Parkf3136042021-03-10 07:49:11 +0900885 private void setGatewayIcmpRuleForProviderInternalNetwork(KubevirtRouter router, KubevirtNetwork network,
886 int tableNum, DeviceId deviceId, boolean install) {
887 MacAddress routerMacAddress = getRouterMacAddress(router);
Daniel Park2884b232021-03-04 18:58:47 +0900888
Daniel Parkf3136042021-03-10 07:49:11 +0900889 if (routerMacAddress == null) {
Daniel Park2884b232021-03-04 18:58:47 +0900890 log.error("Setting gateway ICMP rule for internal network because " +
891 "there's no br-int port for device {}", deviceId);
892 return;
893 }
894
Jian Li858ccd72021-02-04 17:25:01 +0900895 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
896 .matchEthType(Ethernet.TYPE_IPV4)
897 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
898 .matchIcmpType(TYPE_ECHO_REQUEST)
899 .matchIcmpCode(CODE_ECHO_REQEUST)
900 .matchIPDst(IpPrefix.valueOf(network.gatewayIp(), 32));
901
902 Device device = deviceService.getDevice(deviceId);
903 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
904 .extension(buildMoveEthSrcToDstExtension(device), device.id())
905 .extension(buildMoveIpSrcToDstExtension(device), device.id())
906 .extension(buildLoadExtension(device,
907 NXM_NX_IP_TTL, DEFAULT_TTL), device.id())
908 .extension(buildLoadExtension(device,
909 NXM_OF_ICMP_TYPE, TYPE_ECHO_REPLY), device.id())
910 .setIpSrc(network.gatewayIp())
Daniel Parkf3136042021-03-10 07:49:11 +0900911 .setEthSrc(routerMacAddress)
Jian Li858ccd72021-02-04 17:25:01 +0900912 .setOutput(PortNumber.IN_PORT);
913
914 flowService.setRule(
915 appId,
916 deviceId,
917 sBuilder.build(),
918 tBuilder.build(),
919 PRIORITY_ICMP_RULE,
Daniel Parka8968802021-02-25 09:14:22 +0900920 tableNum,
Jian Li858ccd72021-02-04 17:25:01 +0900921 install);
922 }
923
Daniel Park2884b232021-03-04 18:58:47 +0900924 private class InternalRouterEventListener implements KubevirtRouterListener {
925 private boolean isRelevantHelper() {
926 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
Daniel Parka8968802021-02-25 09:14:22 +0900927 }
928
Daniel Park2884b232021-03-04 18:58:47 +0900929 @Override
930 public void event(KubevirtRouterEvent event) {
931 switch (event.type()) {
932 case KUBEVIRT_ROUTER_CREATED:
933 eventExecutor.execute(() -> processRouterCreation(event.subject()));
934 break;
935 case KUBEVIRT_ROUTER_REMOVED:
936 eventExecutor.execute(() -> processRouterDeletion(event.subject()));
937 break;
938 case KUBEVIRT_ROUTER_UPDATED:
939 eventExecutor.execute(() -> processRouterUpdate(event.subject()));
940 break;
941 case KUBEVIRT_ROUTER_INTERNAL_NETWORKS_ATTACHED:
942 eventExecutor.execute(() -> processRouterInternalNetworksAttached(event.subject(),
943 event.internal()));
944 break;
945 case KUBEVIRT_ROUTER_INTERNAL_NETWORKS_DETACHED:
946 eventExecutor.execute(() -> processRouterInternalNetworksDetached(event.subject(),
947 event.internal()));
948 break;
949 case KUBEVIRT_GATEWAY_NODE_ATTACHED:
950 eventExecutor.execute(() -> processRouterGatewayNodeAttached(event.subject(),
951 event.gateway()));
952 break;
953 case KUBEVIRT_GATEWAY_NODE_DETACHED:
954 eventExecutor.execute(() -> processRouterGatewayNodeDetached(event.subject(),
955 event.gateway()));
956 break;
957 case KUBEVIRT_GATEWAY_NODE_CHANGED:
958 eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
959 event.gateway()));
960 break;
Daniel Parka8968802021-02-25 09:14:22 +0900961
Daniel Park2884b232021-03-04 18:58:47 +0900962 default:
963 //do nothing
964 break;
965 }
966 }
967
968 private void processRouterCreation(KubevirtRouter router) {
969 // When a router is created, we performs the election process to associate the router
970 // to the specific gateway. After the election, KubevirtNetwork handler installs bunch of rules
971 // to elected gateway node so that VMs associated to the router can ping to their gateway IP.
972 // SNAT and floating ip rule setup is out of this handler's scope and would be done with the other handlers
973 if (!isRelevantHelper()) {
974 return;
975 }
976 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
977 if (electedGw == null) {
978 return;
979 }
980
981 router.internal().forEach(networkName -> {
982 KubevirtNetwork network = networkService.network(networkName);
983
984 if (network != null) {
985 initGatewayNodeForInternalNetwork(network, router, electedGw, true);
986 }
987 });
988 kubevirtRouterService.updateRouter(router.updatedElectedGateway(electedGw.hostname()));
989 }
990
991 private void processRouterDeletion(KubevirtRouter router) {
992 if (!isRelevantHelper()) {
993 return;
994 }
995 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
996 if (electedGw == null) {
997 return;
998 }
999
1000 router.internal().forEach(networkName -> {
1001 KubevirtNetwork network = networkService.network(networkName);
1002
1003 if (network != null) {
1004 initGatewayNodeForInternalNetwork(network, router, electedGw, false);
1005 }
1006 });
1007 }
1008
1009 private void processRouterUpdate(KubevirtRouter router) {
1010 if (!isRelevantHelper()) {
1011 return;
1012 }
1013 if (router.electedGateway() == null) {
1014 return;
1015 }
1016
1017 KubevirtNode electedGw = nodeService.node(router.electedGateway());
1018
1019 router.internal().forEach(networkName -> {
1020 KubevirtNetwork network = networkService.network(networkName);
1021
1022 if (network != null) {
1023 initGatewayNodeForInternalNetwork(network, router, electedGw, true);
1024 }
1025 });
1026 }
1027
1028 private void processRouterInternalNetworksAttached(KubevirtRouter router,
1029 Set<String> attachedInternalNetworks) {
1030 if (!isRelevantHelper()) {
1031 return;
1032 }
1033 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
1034 if (electedGw == null) {
1035 return;
1036 }
1037
1038 attachedInternalNetworks.forEach(networkName -> {
1039 KubevirtNetwork network = networkService.network(networkName);
1040
1041 if (network != null) {
1042 initGatewayNodeForInternalNetwork(network, router, electedGw, true);
1043 }
1044 });
1045 }
1046
1047 private void processRouterInternalNetworksDetached(KubevirtRouter router,
1048 Set<String> detachedInternalNetworks) {
1049 if (!isRelevantHelper()) {
1050 return;
1051 }
1052 KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
1053 if (electedGw == null) {
1054 return;
1055 }
1056
1057 detachedInternalNetworks.forEach(networkName -> {
1058 KubevirtNetwork network = networkService.network(networkName);
1059
1060 if (network != null) {
1061 initGatewayNodeForInternalNetwork(network, router, electedGw, false);
1062 }
Daniel Park2884b232021-03-04 18:58:47 +09001063
Daniel Parkbabde9c2021-03-09 13:37:42 +09001064 removeDetachedInternalNetworkRules(network, router, electedGw);
1065 });
1066 }
1067
Jian Li8f944d42021-03-23 00:43:29 +09001068 private void removeDetachedInternalNetworkRules(KubevirtNetwork removedNetwork,
1069 KubevirtRouter router,
Daniel Parkbabde9c2021-03-09 13:37:42 +09001070 KubevirtNode electedGw) {
Daniel Parkf3136042021-03-10 07:49:11 +09001071 router.internal().stream().filter(networkId -> kubevirtNetworkService.network(networkId) != null)
1072 .forEach(networkId -> {
1073 kubevirtPortService.ports(networkId).forEach(kubevirtPort -> {
1074 setGatewayInterNetworkRoutingFromNetworkToPort(
1075 router, removedNetwork, kubevirtPort, electedGw, false);
1076 });
Daniel Parkbabde9c2021-03-09 13:37:42 +09001077 });
Daniel Park2884b232021-03-04 18:58:47 +09001078 }
1079
1080 private void processRouterGatewayNodeAttached(KubevirtRouter router,
1081 String associatedGateway) {
1082 if (!isRelevantHelper()) {
1083 return;
1084 }
1085
1086 KubevirtNode gatewayNode = nodeService.node(associatedGateway);
1087 if (gatewayNode == null) {
1088 return;
1089 }
1090
1091 router.internal().forEach(networkName -> {
1092 KubevirtNetwork network = networkService.network(networkName);
1093
1094 if (network != null) {
1095 initGatewayNodeForInternalNetwork(network, router, gatewayNode, true);
1096 }
1097 });
1098 }
1099
1100 private void processRouterGatewayNodeDetached(KubevirtRouter router,
1101 String disAssociatedGateway) {
1102 if (!isRelevantHelper()) {
1103 return;
1104 }
1105
1106 KubevirtNode gatewayNode = nodeService.node(disAssociatedGateway);
1107 if (gatewayNode == null) {
1108 return;
1109 }
1110
1111 router.internal().forEach(networkName -> {
1112 KubevirtNetwork network = networkService.network(networkName);
1113
1114 if (network != null) {
1115 initGatewayNodeForInternalNetwork(network, router, gatewayNode, false);
1116 }
1117 });
1118 }
1119
1120 private void processRouterGatewayNodeChanged(KubevirtRouter router,
1121 String disAssociatedGateway) {
1122 if (!isRelevantHelper()) {
1123 return;
1124 }
1125
1126 KubevirtNode oldGatewayNode = nodeService.node(disAssociatedGateway);
1127 if (oldGatewayNode == null) {
1128 return;
1129 }
1130
1131 router.internal().forEach(networkName -> {
1132 KubevirtNetwork network = networkService.network(networkName);
1133
1134 if (network != null) {
1135 initGatewayNodeForInternalNetwork(network, router, oldGatewayNode, false);
1136 }
1137 });
1138
1139 KubevirtNode newGatewayNode = nodeService.node(router.electedGateway());
1140 if (newGatewayNode == null) {
1141 return;
1142 }
1143
1144 router.internal().forEach(networkName -> {
1145 KubevirtNetwork network = networkService.network(networkName);
1146
1147 if (network != null) {
1148 initGatewayNodeForInternalNetwork(network, router, oldGatewayNode, true);
1149 }
1150 });
1151 }
Daniel Parka8968802021-02-25 09:14:22 +09001152 }
1153
Jian Li556709c2021-02-03 17:54:28 +09001154 private class InternalNetworkEventListener implements KubevirtNetworkListener {
1155
1156 private boolean isRelevantHelper() {
1157 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1158 }
1159
1160 @Override
1161 public void event(KubevirtNetworkEvent event) {
1162 switch (event.type()) {
1163 case KUBEVIRT_NETWORK_CREATED:
1164 eventExecutor.execute(() -> processNetworkCreation(event.subject()));
1165 break;
1166 case KUBEVIRT_NETWORK_REMOVED:
1167 eventExecutor.execute(() -> processNetworkRemoval(event.subject()));
1168 break;
1169 case KUBEVIRT_NETWORK_UPDATED:
1170 default:
1171 // do nothing
1172 break;
1173 }
1174 }
1175
1176 private void processNetworkCreation(KubevirtNetwork network) {
1177 if (!isRelevantHelper()) {
1178 return;
1179 }
1180
1181 switch (network.type()) {
1182 case VXLAN:
1183 case GRE:
1184 case GENEVE:
1185 initIntegrationTunnelBridge(network);
1186 break;
1187 case FLAT:
Jian Li2ce718e2021-02-17 20:42:15 +09001188 case VLAN:
Daniel Parka8968802021-02-25 09:14:22 +09001189 break;
Jian Li556709c2021-02-03 17:54:28 +09001190 default:
1191 // do nothing
1192 break;
1193 }
1194 }
1195
1196 private void processNetworkRemoval(KubevirtNetwork network) {
1197 if (!isRelevantHelper()) {
1198 return;
1199 }
1200
1201 switch (network.type()) {
1202 case VXLAN:
1203 case GRE:
1204 case GENEVE:
1205 purgeIntegrationTunnelBridge(network);
1206 break;
1207 case FLAT:
Jian Li2ce718e2021-02-17 20:42:15 +09001208 case VLAN:
Daniel Parka8968802021-02-25 09:14:22 +09001209 break;
Jian Li556709c2021-02-03 17:54:28 +09001210 default:
1211 // do nothing
1212 break;
1213 }
1214 }
1215
1216 private void initIntegrationTunnelBridge(KubevirtNetwork network) {
1217 if (network.segmentId() == null) {
1218 return;
1219 }
1220
Jian Li94b6d162021-04-15 17:09:11 +09001221 nodeService.completeNodes(WORKER).forEach(n -> {
Jian Li556709c2021-02-03 17:54:28 +09001222 createBridge(n, network);
Daniel Parkf3136042021-03-10 07:49:11 +09001223 createPatchTenantInterface(n, network);
1224 setDefaultRulesForTenantNetwork(n, network);
Jian Li556709c2021-02-03 17:54:28 +09001225 });
1226 }
1227
1228 private void purgeIntegrationTunnelBridge(KubevirtNetwork network) {
1229 if (network.segmentId() == null) {
1230 return;
1231 }
1232
1233 nodeService.completeNodes().forEach(n -> {
1234 removePatchInterface(n, network);
1235 removeBridge(n, network);
1236 });
1237 }
1238 }
1239
1240 private class InternalNodeEventListener implements KubevirtNodeListener {
1241
1242 private boolean isRelevantHelper() {
1243 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1244 }
1245
1246 @Override
1247 public void event(KubevirtNodeEvent event) {
1248 switch (event.type()) {
1249 case KUBEVIRT_NODE_COMPLETE:
1250 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
1251 break;
Daniel Park2884b232021-03-04 18:58:47 +09001252 case KUBEVIRT_NODE_REMOVED:
1253 eventExecutor.execute(() -> processNodeDeletion(event.subject()));
1254 break;
Jian Li556709c2021-02-03 17:54:28 +09001255 case KUBEVIRT_NODE_INCOMPLETE:
1256 case KUBEVIRT_NODE_UPDATED:
1257 default:
1258 // do nothing
1259 break;
1260 }
1261 }
1262
1263 private void processNodeCompletion(KubevirtNode node) {
1264 if (!isRelevantHelper()) {
1265 return;
1266 }
1267
Daniel Parka8968802021-02-25 09:14:22 +09001268 if (node.type().equals(WORKER)) {
1269 for (KubevirtNetwork network : networkService.networks()) {
1270 switch (network.type()) {
1271 case VXLAN:
1272 case GRE:
1273 case GENEVE:
1274 if (network.segmentId() == null) {
1275 continue;
1276 }
1277 createBridge(node, network);
Daniel Parkf3136042021-03-10 07:49:11 +09001278 createPatchTenantInterface(node, network);
1279 setDefaultRulesForTenantNetwork(node, network);
Jian Li8f944d42021-03-23 00:43:29 +09001280 setGatewayArpRulesForTenantNetwork(node, network);
1281 setGatewayIcmpRulesForTenantNetwork(node, network);
1282 setGatewayRuleToWorkerNodeWhenNodeCreated(node, network);
Daniel Parka8968802021-02-25 09:14:22 +09001283 break;
1284 case FLAT:
1285 case VLAN:
1286 default:
1287 // do nothing
1288 break;
1289 }
1290 }
1291 } else if (node.type().equals(GATEWAY)) {
Daniel Park2884b232021-03-04 18:58:47 +09001292 updateGatewayNodeForRouter();
Daniel Parka8968802021-02-25 09:14:22 +09001293 for (KubevirtNetwork network : networkService.networks()) {
1294 switch (network.type()) {
1295 case FLAT:
1296 case VLAN:
Daniel Parka8968802021-02-25 09:14:22 +09001297 break;
1298 case VXLAN:
1299 case GRE:
1300 case GENEVE:
1301 default:
1302 // do nothing
1303 break;
1304 }
Jian Li556709c2021-02-03 17:54:28 +09001305 }
1306 }
1307 }
Daniel Park2884b232021-03-04 18:58:47 +09001308
1309 private void processNodeDeletion(KubevirtNode node) {
1310 if (!isRelevantHelper()) {
1311 return;
1312 }
1313
1314 if (node.type().equals(GATEWAY)) {
1315 updateGatewayNodeForRouter();
1316 for (KubevirtNetwork network : networkService.networks()) {
1317 switch (network.type()) {
1318 case FLAT:
1319 case VLAN:
1320 break;
1321 case VXLAN:
1322 case GRE:
1323 case GENEVE:
1324 default:
1325 // do nothing
1326 break;
1327 }
1328 }
1329 }
1330 }
1331
1332 private void updateGatewayNodeForRouter() {
1333 kubevirtRouterService.routers().forEach(router -> {
1334 KubevirtNode newGwNode = gatewayNodeForSpecifiedRouter(nodeService, router);
1335
1336 if (newGwNode == null) {
1337 return;
1338 }
1339 kubevirtRouterService.updateRouter(router.updatedElectedGateway(newGwNode.hostname()));
1340 });
1341 }
Jian Li556709c2021-02-03 17:54:28 +09001342 }
Daniel Parkbabde9c2021-03-09 13:37:42 +09001343
1344 private class InternalKubevirtPortListener implements KubevirtPortListener {
1345
1346 private boolean isRelevantHelper() {
1347 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
1348 }
1349
1350 @Override
1351 public void event(KubevirtPortEvent event) {
1352 switch (event.type()) {
1353 case KUBEVIRT_PORT_CREATED:
1354 eventExecutor.execute(() -> processPortCreation(event.subject()));
1355 break;
1356 case KUBEVIRT_PORT_UPDATED:
1357 eventExecutor.execute(() -> processPortUpdate(event.subject()));
1358 break;
1359 case KUBEVIRT_PORT_REMOVED:
1360 eventExecutor.execute(() -> processPortDeletion(event.subject()));
1361 break;
1362 default:
1363 //do nothing
1364 break;
1365 }
1366 }
1367
1368 private void processPortCreation(KubevirtPort kubevirtPort) {
1369 if (!isRelevantHelper()) {
1370 return;
1371 }
1372
1373 KubevirtRouter router = getRouterForKubevirtPort(kubevirtRouterService, kubevirtPort);
1374 if (router == null) {
1375 return;
1376 }
1377
1378 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
1379
1380 if (gwNode != null) {
1381
1382 router.internal().forEach(srcNetwork -> {
1383 if (srcNetwork.equals(kubevirtPort.networkId())
1384 || kubevirtNetworkService.network(srcNetwork) == null) {
1385 return;
1386 }
Jian Li8f944d42021-03-23 00:43:29 +09001387 setGatewayInterNetworkRoutingFromNetworkToPort(router,
1388 kubevirtNetworkService.network(srcNetwork),
Daniel Parkbabde9c2021-03-09 13:37:42 +09001389 kubevirtPort, gwNode, true);
1390 });
1391 }
1392 }
1393
1394 private void processPortUpdate(KubevirtPort kubevirtPort) {
1395 if (!isRelevantHelper()) {
1396 return;
1397 }
1398
1399 KubevirtRouter router = getRouterForKubevirtPort(kubevirtRouterService, kubevirtPort);
1400 if (router == null) {
1401 return;
1402 }
1403
1404 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
1405
1406 if (gwNode != null) {
1407
1408 router.internal().forEach(srcNetwork -> {
1409 if (srcNetwork.equals(kubevirtPort.networkId())
1410 || kubevirtNetworkService.network(srcNetwork) == null) {
1411 return;
1412 }
Jian Li8f944d42021-03-23 00:43:29 +09001413 setGatewayInterNetworkRoutingFromNetworkToPort(router,
1414 kubevirtNetworkService.network(srcNetwork),
Daniel Parkbabde9c2021-03-09 13:37:42 +09001415 kubevirtPort, gwNode, true);
1416 });
1417 }
1418 }
1419
1420 private void processPortDeletion(KubevirtPort kubevirtPort) {
1421 if (!isRelevantHelper()) {
1422 return;
1423 }
1424
1425 KubevirtRouter router = getRouterForKubevirtPort(kubevirtRouterService, kubevirtPort);
1426 if (router == null) {
1427 return;
1428 }
1429
1430 KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
1431
1432 if (gwNode != null) {
1433
1434 router.internal().forEach(srcNetwork -> {
1435 if (srcNetwork.equals(kubevirtPort.networkId())
1436 || kubevirtNetworkService.network(srcNetwork) == null) {
1437 return;
1438 }
Jian Li8f944d42021-03-23 00:43:29 +09001439 setGatewayInterNetworkRoutingFromNetworkToPort(router,
1440 kubevirtNetworkService.network(srcNetwork),
Daniel Parkbabde9c2021-03-09 13:37:42 +09001441 kubevirtPort, gwNode, false);
1442 });
1443 }
1444
1445 }
1446 }
Jian Li556709c2021-02-03 17:54:28 +09001447}