blob: b5ba85ecb6f440c7c97f04fb10c5a0bc46defc16 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +09003 *
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.openstacknetworking.impl;
17
18import com.google.common.base.Strings;
19import com.google.common.collect.ImmutableSet;
Daniel Park613ac372018-06-28 14:30:11 +090020import com.google.common.collect.Sets;
Hyunsun Moon44aac662017-02-18 02:07:01 +090021import org.onlab.packet.Ethernet;
22import org.onlab.packet.IPv4;
23import org.onlab.packet.IpAddress;
24import org.onlab.packet.IpPrefix;
sangho072c4dd2017-05-17 10:45:21 +090025import org.onlab.packet.MacAddress;
daniel parkee8700b2017-05-11 15:50:03 +090026import org.onlab.packet.VlanId;
sanghoe765ce22017-06-23 17:54:57 +090027import org.onlab.util.Tools;
28import org.onosproject.cfg.ComponentConfigService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090029import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
sangho072c4dd2017-05-17 10:45:21 +090034import org.onosproject.mastership.MastershipService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090035import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
sangho072c4dd2017-05-17 10:45:21 +090037import org.onosproject.net.device.DeviceService;
38import org.onosproject.net.driver.DriverService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090039import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
sangho072c4dd2017-05-17 10:45:21 +090043import org.onosproject.net.flow.instructions.ExtensionTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.openstacknetworking.api.Constants;
daniel park576969a2018-03-09 07:07:41 +090045import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
sangho072c4dd2017-05-17 10:45:21 +090046import org.onosproject.openstacknetworking.api.InstancePort;
47import org.onosproject.openstacknetworking.api.InstancePortEvent;
48import org.onosproject.openstacknetworking.api.InstancePortListener;
49import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090050import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
daniel park32b42202018-03-14 16:53:44 +090051import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
53import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
54import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Li26949762018-03-30 15:46:37 +090055import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
Hyunsun Moon0d457362017-06-27 17:19:41 +090056import org.onosproject.openstacknode.api.OpenstackNode;
57import org.onosproject.openstacknode.api.OpenstackNode.NetworkMode;
58import org.onosproject.openstacknode.api.OpenstackNodeEvent;
59import org.onosproject.openstacknode.api.OpenstackNodeListener;
60import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090061import org.openstack4j.model.network.ExternalGateway;
62import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090063import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090064import org.openstack4j.model.network.Router;
65import org.openstack4j.model.network.RouterInterface;
66import org.openstack4j.model.network.Subnet;
sanghoe765ce22017-06-23 17:54:57 +090067import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068import org.osgi.service.component.annotations.Activate;
69import org.osgi.service.component.annotations.Component;
70import org.osgi.service.component.annotations.Deactivate;
71import org.osgi.service.component.annotations.Modified;
72import org.osgi.service.component.annotations.Reference;
73import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon44aac662017-02-18 02:07:01 +090074import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
sanghoe765ce22017-06-23 17:54:57 +090077import java.util.Dictionary;
Hyunsun Moon44aac662017-02-18 02:07:01 +090078import java.util.Objects;
sangho072c4dd2017-05-17 10:45:21 +090079import java.util.Optional;
Hyunsun Moon44aac662017-02-18 02:07:01 +090080import java.util.Set;
81import java.util.concurrent.ExecutorService;
82import java.util.stream.Collectors;
83
84import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
85import static org.onlab.util.Tools.groupedThreads;
sangho072c4dd2017-05-17 10:45:21 +090086import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_EXTERNAL_ROUTER_MAC;
87import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
sangho072c4dd2017-05-17 10:45:21 +090088import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
89import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
daniel park796c2eb2018-03-22 17:01:51 +090090import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ADMIN_RULE;
sangho072c4dd2017-05-17 10:45:21 +090091import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_ROUTING_RULE;
92import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_RULE;
93import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_INTERNAL_ROUTING_RULE;
sanghoe765ce22017-06-23 17:54:57 +090094import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
sangho072c4dd2017-05-17 10:45:21 +090095import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE;
96import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
Jian Li8abf2fe2018-06-12 18:42:30 +090097import static org.onosproject.openstacknetworking.api.Constants.STAT_OUTBOUND_TABLE;
Jian Lic2403592018-07-18 12:56:45 +090098import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Ray Milkey8e406512018-10-24 15:56:50 -070099import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_STATEFUL_SNAT;
100import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_STATEFUL_SNAT_DEFAULT;
Jian Li26949762018-03-30 15:46:37 +0900101import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900102import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
103import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Jian Li32b03622018-11-06 17:54:24 +0900104import static org.openstack4j.model.network.NetworkType.FLAT;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105
106/**
107 * Handles OpenStack router events.
108 */
Ray Milkey8e406512018-10-24 15:56:50 -0700109@Component(
110 immediate = true,
111 property = {
112 USE_STATEFUL_SNAT + ":Boolean=" + USE_STATEFUL_SNAT_DEFAULT
113 }
114)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900115public class OpenstackRoutingHandler {
116
117 private final Logger log = LoggerFactory.getLogger(getClass());
118
119 private static final String MSG_ENABLED = "Enabled ";
120 private static final String MSG_DISABLED = "Disabled ";
daniel parkee8700b2017-05-11 15:50:03 +0900121 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
sanghoe765ce22017-06-23 17:54:57 +0900122
Jian Li4d138702018-11-27 17:25:28 +0900123 private static final int VM_PREFIX = 32;
124
Ray Milkey8e406512018-10-24 15:56:50 -0700125 /** Use Stateful SNAT for source NATing. */
126 private boolean useStatefulSnat = USE_STATEFUL_SNAT_DEFAULT;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129 protected CoreService coreService;
130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900132 protected LeadershipService leadershipService;
133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900135 protected ClusterService clusterService;
136
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900138 protected OpenstackNodeService osNodeService;
139
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 @Reference(cardinality = ReferenceCardinality.MANDATORY)
daniel park32b42202018-03-14 16:53:44 +0900141 protected OpenstackNetworkAdminService osNetworkAdminService;
142
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900144 protected OpenstackRouterService osRouterService;
145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho072c4dd2017-05-17 10:45:21 +0900147 protected InstancePortService instancePortService;
148
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho072c4dd2017-05-17 10:45:21 +0900150 protected DeviceService deviceService;
151
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900153 protected OpenstackFlowRuleService osFlowRuleService;
154
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho072c4dd2017-05-17 10:45:21 +0900156 protected MastershipService mastershipService;
157
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho072c4dd2017-05-17 10:45:21 +0900159 protected DriverService driverService;
160
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghoe765ce22017-06-23 17:54:57 +0900162 protected ComponentConfigService configService;
163
Hyunsun Moon44aac662017-02-18 02:07:01 +0900164 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
165 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
166 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
167 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
sangho072c4dd2017-05-17 10:45:21 +0900168 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169
170 private ApplicationId appId;
171 private NodeId localNodeId;
172
173 @Activate
174 protected void activate() {
175 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
176 localNodeId = clusterService.getLocalNode().id();
177 leadershipService.runForLeadership(appId.name());
178 osNodeService.addListener(osNodeListener);
179 osRouterService.addListener(osRouterListener);
sangho072c4dd2017-05-17 10:45:21 +0900180 instancePortService.addListener(instancePortListener);
sanghoe765ce22017-06-23 17:54:57 +0900181 configService.registerProperties(getClass());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900182
183 log.info("Started");
184 }
185
186 @Deactivate
187 protected void deactivate() {
188 osRouterService.removeListener(osRouterListener);
189 osNodeService.removeListener(osNodeListener);
sangho072c4dd2017-05-17 10:45:21 +0900190 instancePortService.removeListener(instancePortListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900191 leadershipService.withdraw(appId.name());
sanghoe765ce22017-06-23 17:54:57 +0900192 configService.unregisterProperties(getClass(), false);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900193 eventExecutor.shutdown();
194
195 log.info("Stopped");
196 }
197
sanghoe765ce22017-06-23 17:54:57 +0900198 @Modified
199 protected void modified(ComponentContext context) {
200 Dictionary<?, ?> properties = context.getProperties();
201 Boolean flag;
202
Ray Milkey8e406512018-10-24 15:56:50 -0700203 flag = Tools.isPropertyEnabled(properties, USE_STATEFUL_SNAT);
sanghoe765ce22017-06-23 17:54:57 +0900204 if (flag == null) {
205 log.info("useStatefulSnat is not configured, " +
206 "using current value of {}", useStatefulSnat);
207 } else {
208 useStatefulSnat = flag;
209 log.info("Configured. useStatefulSnat is {}",
210 useStatefulSnat ? "enabled" : "disabled");
211 }
212
213 resetSnatRules();
214 }
215
Hyunsun Moon44aac662017-02-18 02:07:01 +0900216 private void routerUpdated(Router osRouter) {
217 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
Frank Wang245a6822017-06-14 09:51:35 +0800218 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900219 Network network = osNetworkAdminService.network(
220 osNetworkAdminService.subnet(iface.getSubnetId())
Frank Wang245a6822017-06-14 09:51:35 +0800221 .getNetworkId());
Jian Li5ecfd1a2018-12-10 11:41:03 +0900222 setRouterAdminRules(network.getProviderSegID(),
223 network.getNetworkType(), !osRouter.isAdminStateUp());
Frank Wang245a6822017-06-14 09:51:35 +0800224 });
225
Jian Li5ecfd1a2018-12-10 11:41:03 +0900226 ExternalPeerRouter externalPeerRouter =
227 osNetworkAdminService.externalPeerRouter(exGateway);
Jian Li5e2ad4a2018-07-16 13:40:53 +0900228 VlanId vlanId = externalPeerRouter == null ? VlanId.NONE : externalPeerRouter.vlanId();
daniel park576969a2018-03-09 07:07:41 +0900229
Hyunsun Moon44aac662017-02-18 02:07:01 +0900230 if (exGateway == null) {
Daniel Park613ac372018-06-28 14:30:11 +0900231 deleteUnassociatedExternalPeerRouter();
Jian Li5ecfd1a2018-12-10 11:41:03 +0900232 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface ->
233 setSourceNat(iface, false));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900234 } else {
daniel park32b42202018-03-14 16:53:44 +0900235 osNetworkAdminService.deriveExternalPeerRouterMac(exGateway, osRouter, vlanId);
daniel park576969a2018-03-09 07:07:41 +0900236 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface ->
237 setSourceNat(iface, exGateway.isEnableSnat()));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900238 }
239 }
240
Daniel Park613ac372018-06-28 14:30:11 +0900241 private void deleteUnassociatedExternalPeerRouter() {
242 log.trace("Deleting unassociated external peer router");
243
244 try {
245 Set<String> routerIps = Sets.newConcurrentHashSet();
246
247 osRouterService.routers().stream()
248 .filter(router -> getGatewayIpAddress(router) != null)
249 .map(router -> getGatewayIpAddress(router).toString())
250 .forEach(routerIps::add);
251
252 osNetworkAdminService.externalPeerRouters().stream()
253 .filter(externalPeerRouter ->
Jian Li5e2ad4a2018-07-16 13:40:53 +0900254 !routerIps.contains(externalPeerRouter.ipAddress().toString()))
Daniel Park613ac372018-06-28 14:30:11 +0900255 .forEach(externalPeerRouter -> {
256 osNetworkAdminService
Jian Li5ecfd1a2018-12-10 11:41:03 +0900257 .deleteExternalPeerRouter(
258 externalPeerRouter.ipAddress().toString());
Daniel Park613ac372018-06-28 14:30:11 +0900259 log.trace("Deleted unassociated external peer router {}",
Jian Li5e2ad4a2018-07-16 13:40:53 +0900260 externalPeerRouter.ipAddress().toString());
Daniel Park613ac372018-06-28 14:30:11 +0900261 });
262 } catch (Exception e) {
263 log.error("Exception occurred because of {}", e.toString());
264 }
265 }
266
Frank Wang245a6822017-06-14 09:51:35 +0800267 private void routerRemove(Router osRouter) {
268 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
Jian Li5ecfd1a2018-12-10 11:41:03 +0900269 Network network = osNetworkAdminService.network(
270 osNetworkAdminService.subnet(iface.getSubnetId())
Frank Wang245a6822017-06-14 09:51:35 +0800271 .getNetworkId());
272 setRouterAdminRules(network.getProviderSegID(), network.getNetworkType(), false);
273 });
274 }
275
Hyunsun Moon44aac662017-02-18 02:07:01 +0900276 private void routerIfaceAdded(Router osRouter, RouterInterface osRouterIface) {
Jian Liedc8b762018-03-22 15:42:00 +0900277 Subnet osSubnet = osNetworkAdminService.subnet(osRouterIface.getSubnetId());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900278 if (osSubnet == null) {
279 final String error = String.format(
Jian Li71670d12018-03-02 21:31:07 +0900280 "Failed to set flows for router %s: subnet %s does not exist",
Hyunsun Moon44aac662017-02-18 02:07:01 +0900281 osRouterIface.getId(),
282 osRouterIface.getSubnetId());
283 throw new IllegalStateException(error);
284 }
Frank Wang245a6822017-06-14 09:51:35 +0800285
286 if (!osRouter.isAdminStateUp()) {
Jian Liedc8b762018-03-22 15:42:00 +0900287 Network network = osNetworkAdminService.network(osSubnet.getNetworkId());
Jian Li5ecfd1a2018-12-10 11:41:03 +0900288 setRouterAdminRules(network.getProviderSegID(),
289 network.getNetworkType(), true);
Frank Wang245a6822017-06-14 09:51:35 +0800290 }
291
Hyunsun Moon44aac662017-02-18 02:07:01 +0900292 setInternalRoutes(osRouter, osSubnet, true);
Daniel Park51f9d1e2018-10-26 13:39:09 +0900293 setGatewayIcmp(osSubnet, osRouter, true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900294 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
295 if (exGateway != null && exGateway.isEnableSnat()) {
daniel park576969a2018-03-09 07:07:41 +0900296 setSourceNat(osRouterIface, true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900297 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900298 log.info("Connected subnet({}) to {}", osSubnet.getCidr(), osRouter.getName());
299 }
300
301 private void routerIfaceRemoved(Router osRouter, RouterInterface osRouterIface) {
Jian Liedc8b762018-03-22 15:42:00 +0900302 Subnet osSubnet = osNetworkAdminService.subnet(osRouterIface.getSubnetId());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900303 if (osSubnet == null) {
304 final String error = String.format(
Jian Li71670d12018-03-02 21:31:07 +0900305 "Failed to set flows for router %s: subnet %s does not exist",
Hyunsun Moon44aac662017-02-18 02:07:01 +0900306 osRouterIface.getId(),
307 osRouterIface.getSubnetId());
308 throw new IllegalStateException(error);
309 }
310
Frank Wang245a6822017-06-14 09:51:35 +0800311 if (!osRouter.isAdminStateUp()) {
Jian Liedc8b762018-03-22 15:42:00 +0900312 Network network = osNetworkAdminService.network(osSubnet.getNetworkId());
Jian Li5ecfd1a2018-12-10 11:41:03 +0900313 setRouterAdminRules(network.getProviderSegID(),
314 network.getNetworkType(), false);
Frank Wang245a6822017-06-14 09:51:35 +0800315 }
316
Hyunsun Moon44aac662017-02-18 02:07:01 +0900317 setInternalRoutes(osRouter, osSubnet, false);
Daniel Park51f9d1e2018-10-26 13:39:09 +0900318 setGatewayIcmp(osSubnet, osRouter, false);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900319 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
320 if (exGateway != null && exGateway.isEnableSnat()) {
daniel park576969a2018-03-09 07:07:41 +0900321 setSourceNat(osRouterIface, false);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900322 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900323 log.info("Disconnected subnet({}) from {}", osSubnet.getCidr(), osRouter.getName());
324 }
325
daniel park576969a2018-03-09 07:07:41 +0900326 private void setSourceNat(RouterInterface routerIface, boolean install) {
Jian Liedc8b762018-03-22 15:42:00 +0900327 Subnet osSubnet = osNetworkAdminService.subnet(routerIface.getSubnetId());
328 Network osNet = osNetworkAdminService.network(osSubnet.getNetworkId());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900329
Hyunsun Moon0d457362017-06-27 17:19:41 +0900330 osNodeService.completeNodes(COMPUTE).forEach(cNode -> {
331 setRulesToGateway(cNode, osNet.getProviderSegID(),
332 IpPrefix.valueOf(osSubnet.getCidr()), osNet.getNetworkType(),
333 install);
334 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900335
sanghoe765ce22017-06-23 17:54:57 +0900336 if (useStatefulSnat) {
337 setStatefulSnatRules(routerIface, install);
338 } else {
339 setReactiveSnatRules(routerIface, install);
340 }
341
342 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
343 log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
344 }
345
346 private void setStatefulSnatRules(RouterInterface routerIface, boolean install) {
Jian Liedc8b762018-03-22 15:42:00 +0900347 Subnet osSubnet = osNetworkAdminService.subnet(routerIface.getSubnetId());
348 Network osNet = osNetworkAdminService.network(osSubnet.getNetworkId());
sanghoe765ce22017-06-23 17:54:57 +0900349
Jian Li32b03622018-11-06 17:54:24 +0900350 if (osNet.getNetworkType() == FLAT) {
daniel park796c2eb2018-03-22 17:01:51 +0900351 return;
352 }
353
sanghoe765ce22017-06-23 17:54:57 +0900354 Optional<Router> osRouter = osRouterService.routers().stream()
355 .filter(router -> osRouterService.routerInterfaces(routerIface.getId()) != null)
356 .findAny();
357
358 if (!osRouter.isPresent()) {
359 log.error("Cannot find a router for router interface {} ", routerIface);
360 return;
361 }
362 IpAddress natAddress = getGatewayIpAddress(osRouter.get());
sangho072c4dd2017-05-17 10:45:21 +0900363 if (natAddress == null) {
364 return;
365 }
Jian Liedc8b762018-03-22 15:42:00 +0900366 String netId = osNetworkAdminService.subnet(routerIface.getSubnetId()).getNetworkId();
sangho072c4dd2017-05-17 10:45:21 +0900367
368 osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY)
369 .forEach(gwNode -> {
daniel park576969a2018-03-09 07:07:41 +0900370 instancePortService.instancePorts(netId)
Jian Lic2403592018-07-18 12:56:45 +0900371 .stream()
372 .filter(port -> port.state() == ACTIVE)
daniel park576969a2018-03-09 07:07:41 +0900373 .forEach(port -> setRulesForSnatIngressRule(gwNode.intgBridge(),
sangho072c4dd2017-05-17 10:45:21 +0900374 Long.parseLong(osNet.getProviderSegID()),
Jian Li4d138702018-11-27 17:25:28 +0900375 IpPrefix.valueOf(port.ipAddress(), VM_PREFIX),
sangho072c4dd2017-05-17 10:45:21 +0900376 port.deviceId(),
377 install));
378
379 setOvsNatIngressRule(gwNode.intgBridge(),
Jian Li4d138702018-11-27 17:25:28 +0900380 IpPrefix.valueOf(natAddress, VM_PREFIX),
sangho072c4dd2017-05-17 10:45:21 +0900381 Constants.DEFAULT_EXTERNAL_ROUTER_MAC, install);
382 setOvsNatEgressRule(gwNode.intgBridge(),
383 natAddress, Long.parseLong(osNet.getProviderSegID()),
384 gwNode.patchPortNum(), install);
385 });
sanghoe765ce22017-06-23 17:54:57 +0900386 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900387
sanghoe765ce22017-06-23 17:54:57 +0900388 private void setReactiveSnatRules(RouterInterface routerIface, boolean install) {
Jian Liedc8b762018-03-22 15:42:00 +0900389 Subnet osSubnet = osNetworkAdminService.subnet(routerIface.getSubnetId());
390 Network osNet = osNetworkAdminService.network(osSubnet.getNetworkId());
sanghoe765ce22017-06-23 17:54:57 +0900391
392 osNodeService.completeNodes(GATEWAY)
393 .forEach(gwNode -> setRulesToController(
394 gwNode.intgBridge(),
395 osNet.getProviderSegID(),
396 IpPrefix.valueOf(osSubnet.getCidr()),
397 osNet.getNetworkType(),
398 install));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900399 }
400
sangho072c4dd2017-05-17 10:45:21 +0900401 private IpAddress getGatewayIpAddress(Router osRouter) {
402
Daniel Park613ac372018-06-28 14:30:11 +0900403 if (osRouter.getExternalGatewayInfo() == null) {
404 return null;
405 }
Jian Li5ecfd1a2018-12-10 11:41:03 +0900406 String extNetId = osNetworkAdminService.network(
407 osRouter.getExternalGatewayInfo().getNetworkId()).getId();
Jian Liedc8b762018-03-22 15:42:00 +0900408 Optional<Subnet> extSubnet = osNetworkAdminService.subnets().stream()
sangho072c4dd2017-05-17 10:45:21 +0900409 .filter(subnet -> subnet.getNetworkId().equals(extNetId))
410 .findAny();
411
412 if (!extSubnet.isPresent()) {
413 log.error("Cannot find externel subnet for the router");
414 return null;
415 }
416
417 return IpAddress.valueOf(extSubnet.get().getGateway());
418 }
419
sanghoe765ce22017-06-23 17:54:57 +0900420 private void resetSnatRules() {
421 if (useStatefulSnat) {
422 osRouterService.routerInterfaces().forEach(
423 routerIface -> {
424 setReactiveSnatRules(routerIface, false);
425 setStatefulSnatRules(routerIface, true);
426 }
427 );
428 } else {
429 osRouterService.routerInterfaces().forEach(
430 routerIface -> {
431 setStatefulSnatRules(routerIface, false);
432 setReactiveSnatRules(routerIface, true);
433 }
434 );
435 }
436 }
437
Daniel Park51f9d1e2018-10-26 13:39:09 +0900438 private void setGatewayIcmp(Subnet osSubnet, Router osRouter, boolean install) {
Jian Li4d138702018-11-27 17:25:28 +0900439 OpenstackNode srcNatGw = osNodeService.completeNodes(GATEWAY)
440 .stream().findFirst().orElse(null);
daniel parkb5817102018-02-15 00:18:51 +0900441
Jian Li4d138702018-11-27 17:25:28 +0900442 if (srcNatGw == null) {
daniel parkb5817102018-02-15 00:18:51 +0900443 return;
444 }
445
Hyunsun Moon44aac662017-02-18 02:07:01 +0900446 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
447 // do nothing if no gateway is set
448 return;
449 }
450
451 // take ICMP request to a subnet gateway through gateway node group
Jian Li4d138702018-11-27 17:25:28 +0900452 Network net = osNetworkAdminService.network(osSubnet.getNetworkId());
Daniel Park51f9d1e2018-10-26 13:39:09 +0900453 Set<Subnet> routableSubnets = routableSubnets(osRouter, osSubnet.getId());
454
Jian Li4d138702018-11-27 17:25:28 +0900455 switch (net.getNetworkType()) {
daniel parkee8700b2017-05-11 15:50:03 +0900456 case VXLAN:
Jian Li4d138702018-11-27 17:25:28 +0900457 setGatewayIcmpForVxlan(osSubnet, srcNatGw, net, routableSubnets, install);
daniel parkee8700b2017-05-11 15:50:03 +0900458 break;
459 case VLAN:
Jian Li4d138702018-11-27 17:25:28 +0900460 setGatewayIcmpForVlan(osSubnet, srcNatGw, net, routableSubnets, install);
daniel parkee8700b2017-05-11 15:50:03 +0900461 break;
462 default:
Jian Li4d138702018-11-27 17:25:28 +0900463 final String error = String.format("%s %s", ERR_UNSUPPORTED_NET_TYPE,
464 net.getNetworkType().toString());
daniel parkee8700b2017-05-11 15:50:03 +0900465 throw new IllegalStateException(error);
466 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900467
468 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
daniel park576969a2018-03-09 07:07:41 +0900469 osNodeService.completeNodes(GATEWAY).forEach(gNode ->
Hyunsun Moon0d457362017-06-27 17:19:41 +0900470 setGatewayIcmpRule(
471 gatewayIp,
472 gNode.intgBridge(),
daniel park576969a2018-03-09 07:07:41 +0900473 install));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900474
475 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
476 log.debug(updateStr + "ICMP to {}", osSubnet.getGateway());
477 }
478
Jian Li4d138702018-11-27 17:25:28 +0900479 private void setGatewayIcmpForVxlan(Subnet osSubnet,
480 OpenstackNode srcNatGw,
481 Network network,
482 Set<Subnet> routableSubnets,
483 boolean install) {
484 osNodeService.completeNodes(COMPUTE).stream()
485 .filter(cNode -> cNode.dataIp() != null)
486 .forEach(cNode -> setRulesToGatewayWithRoutableSubnets(
487 cNode,
488 srcNatGw,
489 network.getProviderSegID(),
490 osSubnet,
491 routableSubnets,
492 NetworkMode.VXLAN,
493 install));
494 }
495
496 private void setGatewayIcmpForVlan(Subnet osSubnet,
497 OpenstackNode srcNatGw,
498 Network network,
499 Set<Subnet> routableSubnets,
500 boolean install) {
501 osNodeService.completeNodes(COMPUTE).stream()
502 .filter(cNode -> cNode.vlanPortNum() != null)
503 .forEach(cNode -> setRulesToGatewayWithRoutableSubnets(
504 cNode,
505 srcNatGw,
506 network.getProviderSegID(),
507 osSubnet,
508 routableSubnets,
509 NetworkMode.VLAN,
510 install));
511 }
512
Hyunsun Moon44aac662017-02-18 02:07:01 +0900513 private void setInternalRoutes(Router osRouter, Subnet updatedSubnet, boolean install) {
Jian Liedc8b762018-03-22 15:42:00 +0900514 Network updatedNetwork = osNetworkAdminService.network(updatedSubnet.getNetworkId());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900515 Set<Subnet> routableSubnets = routableSubnets(osRouter, updatedSubnet.getId());
Jian Lib6969502018-10-30 20:38:07 +0900516 String updatedSegmentId = getSegmentId(updatedSubnet);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900517
518 // installs rule from/to my subnet intentionally to fix ICMP failure
519 // to my subnet gateway if no external gateway added to the router
Hyunsun Moon0d457362017-06-27 17:19:41 +0900520 osNodeService.completeNodes(COMPUTE).forEach(cNode -> {
521 setInternalRouterRules(
522 cNode.intgBridge(),
Jian Lib6969502018-10-30 20:38:07 +0900523 updatedSegmentId,
524 updatedSegmentId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900525 IpPrefix.valueOf(updatedSubnet.getCidr()),
526 IpPrefix.valueOf(updatedSubnet.getCidr()),
527 updatedNetwork.getNetworkType(),
528 install
529 );
Hyunsun Moon44aac662017-02-18 02:07:01 +0900530
Hyunsun Moon0d457362017-06-27 17:19:41 +0900531 routableSubnets.forEach(subnet -> {
532 setInternalRouterRules(
533 cNode.intgBridge(),
Jian Lib6969502018-10-30 20:38:07 +0900534 updatedSegmentId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900535 getSegmentId(subnet),
536 IpPrefix.valueOf(updatedSubnet.getCidr()),
537 IpPrefix.valueOf(subnet.getCidr()),
538 updatedNetwork.getNetworkType(),
539 install
540 );
541 setInternalRouterRules(
542 cNode.intgBridge(),
543 getSegmentId(subnet),
Jian Lib6969502018-10-30 20:38:07 +0900544 updatedSegmentId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900545 IpPrefix.valueOf(subnet.getCidr()),
546 IpPrefix.valueOf(updatedSubnet.getCidr()),
547 updatedNetwork.getNetworkType(),
548 install
549 );
550 });
551 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900552
daniel parkee8700b2017-05-11 15:50:03 +0900553
Hyunsun Moon44aac662017-02-18 02:07:01 +0900554 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
555 routableSubnets.forEach(subnet -> log.debug(
556 updateStr + "route between subnet:{} and subnet:{}",
557 subnet.getCidr(),
558 updatedSubnet.getCidr()));
559 }
560
561 private Set<Subnet> routableSubnets(Router osRouter, String osSubnetId) {
562 Set<Subnet> osSubnets = osRouterService.routerInterfaces(osRouter.getId())
563 .stream()
564 .filter(iface -> !Objects.equals(iface.getSubnetId(), osSubnetId))
Jian Liedc8b762018-03-22 15:42:00 +0900565 .map(iface -> osNetworkAdminService.subnet(iface.getSubnetId()))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900566 .collect(Collectors.toSet());
567 return ImmutableSet.copyOf(osSubnets);
568 }
569
daniel parkee8700b2017-05-11 15:50:03 +0900570 private String getSegmentId(Subnet osSubnet) {
Jian Liedc8b762018-03-22 15:42:00 +0900571 return osNetworkAdminService.network(osSubnet.getNetworkId()).getProviderSegID();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900572 }
573
574 private void setGatewayIcmpRule(IpAddress gatewayIp, DeviceId deviceId, boolean install) {
575 TrafficSelector selector = DefaultTrafficSelector.builder()
576 .matchEthType(Ethernet.TYPE_IPV4)
577 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900578 .matchIPDst(gatewayIp.getIp4Address().toIpPrefix())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900579 .build();
580
581 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Jian Li4d5c5c32018-04-02 16:38:18 +0900582 .punt()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900583 .build();
584
sanghodc375372017-06-08 10:41:30 +0900585 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900586 appId,
587 deviceId,
588 selector,
589 treatment,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900590 PRIORITY_ICMP_RULE,
sanghodc375372017-06-08 10:41:30 +0900591 Constants.GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900592 install);
593 }
594
Jian Li4d138702018-11-27 17:25:28 +0900595 private void setInternalRouterRules(DeviceId deviceId, String srcSegId, String dstSegId,
daniel parkee8700b2017-05-11 15:50:03 +0900596 IpPrefix srcSubnet, IpPrefix dstSubnet,
597 NetworkType networkType, boolean install) {
Jian Li4d138702018-11-27 17:25:28 +0900598
daniel parkee8700b2017-05-11 15:50:03 +0900599 switch (networkType) {
600 case VXLAN:
Jian Li4d138702018-11-27 17:25:28 +0900601 setInternalRouterRulesForVxlan(deviceId, srcSegId, dstSegId,
602 srcSubnet, dstSubnet, install);
daniel parkee8700b2017-05-11 15:50:03 +0900603 break;
604 case VLAN:
Jian Li4d138702018-11-27 17:25:28 +0900605 setInternalRouterRulesForVlan(deviceId, srcSegId, dstSegId,
606 srcSubnet, dstSubnet, install);
daniel parkee8700b2017-05-11 15:50:03 +0900607 break;
608 default:
Jian Li4d138702018-11-27 17:25:28 +0900609 final String error = String.format("%s %s", ERR_UNSUPPORTED_NET_TYPE,
610 networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900611 throw new IllegalStateException(error);
612 }
613
Hyunsun Moon44aac662017-02-18 02:07:01 +0900614 }
615
Jian Li4d138702018-11-27 17:25:28 +0900616 private void setInternalRouterRulesForVxlan(DeviceId deviceId,
617 String srcSegmentId,
618 String dstSegmentId,
619 IpPrefix srcSubnet,
620 IpPrefix dstSubnet,
621 boolean install) {
622 TrafficSelector selector;
623 TrafficTreatment treatment;
624 selector = DefaultTrafficSelector.builder()
625 .matchEthType(Ethernet.TYPE_IPV4)
626 .matchTunnelId(Long.parseLong(srcSegmentId))
627 .matchIPSrc(srcSubnet.getIp4Prefix())
628 .matchIPDst(dstSubnet.getIp4Prefix())
629 .build();
630
631 treatment = DefaultTrafficTreatment.builder()
632 .setTunnelId(Long.parseLong(dstSegmentId))
633 .transition(STAT_OUTBOUND_TABLE)
634 .build();
635
636 osFlowRuleService.setRule(
637 appId,
638 deviceId,
639 selector,
640 treatment,
641 PRIORITY_INTERNAL_ROUTING_RULE,
642 ROUTING_TABLE,
643 install);
644
645 selector = DefaultTrafficSelector.builder()
646 .matchEthType(Ethernet.TYPE_IPV4)
647 .matchTunnelId(Long.parseLong(dstSegmentId))
648 .matchIPSrc(srcSubnet.getIp4Prefix())
649 .matchIPDst(dstSubnet.getIp4Prefix())
650 .build();
651
652 treatment = DefaultTrafficTreatment.builder()
653 .setTunnelId(Long.parseLong(dstSegmentId))
654 .transition(STAT_OUTBOUND_TABLE)
655 .build();
656
657 osFlowRuleService.setRule(
658 appId,
659 deviceId,
660 selector,
661 treatment,
662 PRIORITY_INTERNAL_ROUTING_RULE,
663 ROUTING_TABLE,
664 install);
665 }
666
667 private void setInternalRouterRulesForVlan(DeviceId deviceId,
668 String srcSegmentId,
669 String dstSegmentId,
670 IpPrefix srcSubnet,
671 IpPrefix dstSubnet,
672 boolean install) {
673 TrafficSelector selector;
674 TrafficTreatment treatment;
675 selector = DefaultTrafficSelector.builder()
676 .matchEthType(Ethernet.TYPE_IPV4)
677 .matchVlanId(VlanId.vlanId(srcSegmentId))
678 .matchIPSrc(srcSubnet.getIp4Prefix())
679 .matchIPDst(dstSubnet.getIp4Prefix())
680 .build();
681
682 treatment = DefaultTrafficTreatment.builder()
683 .setVlanId(VlanId.vlanId(dstSegmentId))
684 .transition(STAT_OUTBOUND_TABLE)
685 .build();
686
687 osFlowRuleService.setRule(
688 appId,
689 deviceId,
690 selector,
691 treatment,
692 PRIORITY_INTERNAL_ROUTING_RULE,
693 ROUTING_TABLE,
694 install);
695
696 selector = DefaultTrafficSelector.builder()
697 .matchEthType(Ethernet.TYPE_IPV4)
698 .matchVlanId(VlanId.vlanId(dstSegmentId))
699 .matchIPSrc(srcSubnet.getIp4Prefix())
700 .matchIPDst(dstSubnet.getIp4Prefix())
701 .build();
702
703 treatment = DefaultTrafficTreatment.builder()
704 .setVlanId(VlanId.vlanId(dstSegmentId))
705 .transition(STAT_OUTBOUND_TABLE)
706 .build();
707
708 osFlowRuleService.setRule(
709 appId,
710 deviceId,
711 selector,
712 treatment,
713 PRIORITY_INTERNAL_ROUTING_RULE,
714 ROUTING_TABLE,
715 install);
716 }
717
Jian Li5ecfd1a2018-12-10 11:41:03 +0900718 private void setRulesToGateway(OpenstackNode osNode,
719 String segmentId,
720 IpPrefix srcSubnet,
721 NetworkType networkType,
722 boolean install) {
723 OpenstackNode sourceNatGateway =
724 osNodeService.completeNodes(GATEWAY).stream().findFirst().orElse(null);
daniel parkb5817102018-02-15 00:18:51 +0900725
726 if (sourceNatGateway == null) {
727 return;
728 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900729
daniel parkee8700b2017-05-11 15:50:03 +0900730 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
731 .matchEthType(Ethernet.TYPE_IPV4)
Hyunsun Moon0d457362017-06-27 17:19:41 +0900732 .matchIPSrc(srcSubnet.getIp4Prefix())
daniel parkee8700b2017-05-11 15:50:03 +0900733 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
734
735 switch (networkType) {
736 case VXLAN:
737 sBuilder.matchTunnelId(Long.parseLong(segmentId));
daniel parkee8700b2017-05-11 15:50:03 +0900738 break;
739 case VLAN:
740 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
daniel parkee8700b2017-05-11 15:50:03 +0900741 break;
742 default:
Jian Li71670d12018-03-02 21:31:07 +0900743 final String error = String.format("%s %s",
744 ERR_UNSUPPORTED_NET_TYPE,
daniel parkee8700b2017-05-11 15:50:03 +0900745 networkType.toString());
746 throw new IllegalStateException(error);
747 }
748
Daniel Parkc64b4c62018-05-09 18:13:39 +0900749 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
750
751 switch (networkType) {
752 case VXLAN:
753 tBuilder.extension(buildExtension(
754 deviceService,
755 osNode.intgBridge(),
756 sourceNatGateway.dataIp().getIp4Address()),
757 osNode.intgBridge())
758 .setOutput(osNode.tunnelPortNum());
759 break;
760
761 case VLAN:
762 tBuilder.setOutput(osNode.vlanPortNum());
763 break;
764
765 default:
766 break;
767 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900768
sanghodc375372017-06-08 10:41:30 +0900769 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900770 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900771 osNode.intgBridge(),
daniel parkee8700b2017-05-11 15:50:03 +0900772 sBuilder.build(),
Daniel Parkc64b4c62018-05-09 18:13:39 +0900773 tBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900774 PRIORITY_EXTERNAL_ROUTING_RULE,
sanghodc375372017-06-08 10:41:30 +0900775 ROUTING_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900776 install);
777 }
778
Jian Li5ecfd1a2018-12-10 11:41:03 +0900779 private void setRulesForSnatIngressRule(DeviceId deviceId,
780 Long vni,
781 IpPrefix destVmIp,
782 DeviceId dstDeviceId,
783 boolean install) {
sangho072c4dd2017-05-17 10:45:21 +0900784
785 TrafficSelector selector = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900786 .matchEthType(Ethernet.TYPE_IPV4)
sangho072c4dd2017-05-17 10:45:21 +0900787 .matchIPDst(destVmIp)
788 .build();
Hyunsun Moon0d457362017-06-27 17:19:41 +0900789
sangho072c4dd2017-05-17 10:45:21 +0900790 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
791 .setTunnelId(vni)
792 .extension(buildExtension(
793 deviceService,
794 deviceId,
795 osNodeService.node(dstDeviceId).dataIp().getIp4Address()),
796 deviceId)
797 .setOutput(osNodeService.node(deviceId).tunnelPortNum())
798 .build();
daniel parkee8700b2017-05-11 15:50:03 +0900799
sanghodc375372017-06-08 10:41:30 +0900800 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900801 appId,
sangho072c4dd2017-05-17 10:45:21 +0900802 deviceId,
803 selector,
804 treatment,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900805 PRIORITY_EXTERNAL_ROUTING_RULE,
sanghodc375372017-06-08 10:41:30 +0900806 Constants.GW_COMMON_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900807 install);
808 }
809
Jian Li5ecfd1a2018-12-10 11:41:03 +0900810 private void setRulesToGatewayWithRoutableSubnets(OpenstackNode osNode,
811 OpenstackNode sourceNatGateway,
812 String segmentId,
813 Subnet updatedSubnet,
814 Set<Subnet> routableSubnets,
815 NetworkMode networkMode,
Daniel Park51f9d1e2018-10-26 13:39:09 +0900816 boolean install) {
817 //At first we install flow rules to gateway with segId and gatewayIp of updated subnet
Jian Li5ecfd1a2018-12-10 11:41:03 +0900818 setRulesToGatewayWithDstIp(osNode, sourceNatGateway, segmentId,
819 IpAddress.valueOf(updatedSubnet.getGateway()), networkMode, install);
Daniel Park51f9d1e2018-10-26 13:39:09 +0900820
821 routableSubnets.forEach(subnet -> {
822 setRulesToGatewayWithDstIp(osNode, sourceNatGateway,
823 segmentId, IpAddress.valueOf(subnet.getGateway()),
824 networkMode, install);
825
826 Network network = osNetworkAdminService.network(subnet.getNetworkId());
827 setRulesToGatewayWithDstIp(osNode, sourceNatGateway,
828 network.getProviderSegID(), IpAddress.valueOf(updatedSubnet.getGateway()),
829 networkMode, install);
830 });
831 }
832
Jian Li5ecfd1a2018-12-10 11:41:03 +0900833 private void setRulesToGatewayWithDstIp(OpenstackNode osNode,
834 OpenstackNode sourceNatGateway,
835 String segmentId,
836 IpAddress dstIp,
837 NetworkMode networkMode,
838 boolean install) {
Daniel Parkc64b4c62018-05-09 18:13:39 +0900839 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
840 .matchEthType(Ethernet.TYPE_IPV4)
841 .matchIPDst(dstIp.getIp4Address().toIpPrefix());
842
Daniel Parkc64b4c62018-05-09 18:13:39 +0900843 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
844
845 switch (networkMode) {
846 case VXLAN:
Daniel Park51f9d1e2018-10-26 13:39:09 +0900847 sBuilder.matchTunnelId(Long.parseLong(segmentId));
Daniel Parkc64b4c62018-05-09 18:13:39 +0900848 tBuilder.extension(buildExtension(
daniel parkb5817102018-02-15 00:18:51 +0900849 deviceService,
850 osNode.intgBridge(),
851 sourceNatGateway.dataIp().getIp4Address()),
852 osNode.intgBridge())
Daniel Parkc64b4c62018-05-09 18:13:39 +0900853 .setOutput(osNode.tunnelPortNum());
854 break;
855
856 case VLAN:
Daniel Park51f9d1e2018-10-26 13:39:09 +0900857 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
Daniel Parkc64b4c62018-05-09 18:13:39 +0900858 tBuilder.setOutput(osNode.vlanPortNum());
859 break;
860
861 default:
862 break;
863 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900864
sanghodc375372017-06-08 10:41:30 +0900865 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900866 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900867 osNode.intgBridge(),
Daniel Parkc64b4c62018-05-09 18:13:39 +0900868 sBuilder.build(),
869 tBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900870 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900871 ROUTING_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900872 install);
873 }
874
Jian Li5ecfd1a2018-12-10 11:41:03 +0900875 private void setOvsNatIngressRule(DeviceId deviceId,
876 IpPrefix cidr,
877 MacAddress dstMac,
878 boolean install) {
sangho072c4dd2017-05-17 10:45:21 +0900879
880 TrafficSelector selector = DefaultTrafficSelector.builder()
881 .matchEthType(Ethernet.TYPE_IPV4)
882 .matchIPDst(cidr)
883 .build();
884
Jian Liedc8b762018-03-22 15:42:00 +0900885 ExtensionTreatment natTreatment = RulePopulatorUtil
886 .niciraConnTrackTreatmentBuilder(driverService, deviceId)
sangho072c4dd2017-05-17 10:45:21 +0900887 .commit(false)
888 .natAction(true)
889 .table((short) 0)
890 .build();
891
892 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
893 .setEthDst(dstMac)
894 .extension(natTreatment, deviceId)
895 .build();
896
897 osFlowRuleService.setRule(
898 appId,
899 deviceId,
900 selector,
901 treatment,
sanghoe765ce22017-06-23 17:54:57 +0900902 PRIORITY_STATEFUL_SNAT_RULE,
sangho072c4dd2017-05-17 10:45:21 +0900903 GW_COMMON_TABLE,
904 install);
905 }
906
Jian Li5ecfd1a2018-12-10 11:41:03 +0900907 private void setOvsNatEgressRule(DeviceId deviceId,
908 IpAddress natAddress,
909 long vni,
910 PortNumber output,
sangho072c4dd2017-05-17 10:45:21 +0900911 boolean install) {
912
913 TrafficSelector selector = DefaultTrafficSelector.builder()
914 .matchEthType(Ethernet.TYPE_IPV4)
915 .matchEthDst(DEFAULT_GATEWAY_MAC)
916 .matchTunnelId(vni)
917 .build();
918
Jian Liedc8b762018-03-22 15:42:00 +0900919 ExtensionTreatment natTreatment = RulePopulatorUtil
920 .niciraConnTrackTreatmentBuilder(driverService, deviceId)
sangho072c4dd2017-05-17 10:45:21 +0900921 .commit(true)
922 .natAction(true)
923 .natIp(natAddress)
924 .build();
925
926 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
927 .extension(natTreatment, deviceId)
928 .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC)
929 .setEthSrc(DEFAULT_GATEWAY_MAC)
930 .setOutput(output)
931 .build();
932
933 osFlowRuleService.setRule(
934 appId,
935 deviceId,
936 selector,
937 treatment,
sanghoe765ce22017-06-23 17:54:57 +0900938 PRIORITY_STATEFUL_SNAT_RULE,
sangho072c4dd2017-05-17 10:45:21 +0900939 GW_COMMON_TABLE,
940 install);
941 }
942
Jian Li5ecfd1a2018-12-10 11:41:03 +0900943 private void setRulesToController(DeviceId deviceId,
944 String segmentId,
945 IpPrefix srcSubnet,
946 NetworkType networkType,
947 boolean install) {
sanghoe765ce22017-06-23 17:54:57 +0900948 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
949 .matchEthType(Ethernet.TYPE_IPV4)
Daniel Parkc64b4c62018-05-09 18:13:39 +0900950 .matchIPSrc(srcSubnet)
951 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
952
sanghoe765ce22017-06-23 17:54:57 +0900953 switch (networkType) {
954 case VXLAN:
Daniel Parkc64b4c62018-05-09 18:13:39 +0900955 sBuilder.matchTunnelId(Long.parseLong(segmentId));
sanghoe765ce22017-06-23 17:54:57 +0900956 break;
957 case VLAN:
Daniel Parkc64b4c62018-05-09 18:13:39 +0900958 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
sanghoe765ce22017-06-23 17:54:57 +0900959 break;
960 default:
Jian Li71670d12018-03-02 21:31:07 +0900961 final String error = String.format("%s %s",
962 ERR_UNSUPPORTED_NET_TYPE,
sanghoe765ce22017-06-23 17:54:57 +0900963 networkType.toString());
964 throw new IllegalStateException(error);
965 }
966
Daniel Parkc64b4c62018-05-09 18:13:39 +0900967 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
sanghoe765ce22017-06-23 17:54:57 +0900968
969 if (networkType.equals(NetworkType.VLAN)) {
970 tBuilder.popVlan();
971 }
972
Jian Li4d5c5c32018-04-02 16:38:18 +0900973 tBuilder.punt();
sanghoe765ce22017-06-23 17:54:57 +0900974
975 osFlowRuleService.setRule(
976 appId,
977 deviceId,
978 sBuilder.build(),
979 tBuilder.build(),
980 PRIORITY_EXTERNAL_ROUTING_RULE,
981 GW_COMMON_TABLE,
982 install);
sanghoe765ce22017-06-23 17:54:57 +0900983 }
sangho072c4dd2017-05-17 10:45:21 +0900984
Jian Li5ecfd1a2018-12-10 11:41:03 +0900985 private void setRouterAdminRules(String segmentId,
986 NetworkType networkType,
987 boolean install) {
Frank Wang245a6822017-06-14 09:51:35 +0800988 TrafficTreatment treatment;
989 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
990 .matchEthType(Ethernet.TYPE_IPV4);
991
992 switch (networkType) {
993 case VXLAN:
994 sBuilder.matchTunnelId(Long.parseLong(segmentId));
995 break;
996 case VLAN:
997 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
998 break;
999 default:
Jian Li71670d12018-03-02 21:31:07 +09001000 final String error = String.format("%s %s",
1001 ERR_UNSUPPORTED_NET_TYPE,
Frank Wang245a6822017-06-14 09:51:35 +08001002 networkType.toString());
1003 throw new IllegalStateException(error);
1004 }
1005
1006 treatment = DefaultTrafficTreatment.builder()
1007 .drop()
1008 .build();
1009
1010 osNodeService.completeNodes().stream()
1011 .filter(osNode -> osNode.type() == COMPUTE)
1012 .forEach(osNode -> {
1013 osFlowRuleService.setRule(
1014 appId,
1015 osNode.intgBridge(),
1016 sBuilder.build(),
1017 treatment,
1018 PRIORITY_ADMIN_RULE,
1019 ROUTING_TABLE,
1020 install);
1021 });
1022 }
1023
Hyunsun Moon44aac662017-02-18 02:07:01 +09001024 private class InternalRouterEventListener implements OpenstackRouterListener {
1025
Jian Li34220ea2018-11-14 01:30:24 +09001026 private boolean isRelevantHelper() {
1027 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001028 }
1029
1030 // FIXME only one leader in the cluster should process
1031 @Override
1032 public void event(OpenstackRouterEvent event) {
1033 switch (event.type()) {
1034 case OPENSTACK_ROUTER_CREATED:
Jian Li4d138702018-11-27 17:25:28 +09001035 eventExecutor.execute(() -> processRouterCreation(event));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001036 break;
1037 case OPENSTACK_ROUTER_UPDATED:
Jian Li4d138702018-11-27 17:25:28 +09001038 eventExecutor.execute(() -> processRouterUpdate(event));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001039 break;
1040 case OPENSTACK_ROUTER_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001041 eventExecutor.execute(() -> processRouterRemoval(event));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001042 break;
1043 case OPENSTACK_ROUTER_INTERFACE_ADDED:
Jian Li4d138702018-11-27 17:25:28 +09001044 eventExecutor.execute(() -> processRouterIntfCreation(event));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001045 break;
1046 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
Jian Li4d138702018-11-27 17:25:28 +09001047 eventExecutor.execute(() -> processRouterIntfUpdate(event));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001048 break;
1049 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
Jian Li4d138702018-11-27 17:25:28 +09001050 eventExecutor.execute(() -> processRouterIntfRemoval(event));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001051 break;
1052 case OPENSTACK_ROUTER_GATEWAY_ADDED:
Jian Li32b03622018-11-06 17:54:24 +09001053 log.debug("Router external gateway {} added",
1054 event.externalGateway().getNetworkId());
Ray Milkey08932a72018-02-20 15:34:38 -08001055 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +09001056 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
Jian Li32b03622018-11-06 17:54:24 +09001057 log.debug("Router external gateway {} removed",
1058 event.externalGateway().getNetworkId());
Ray Milkey08932a72018-02-20 15:34:38 -08001059 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +09001060 case OPENSTACK_FLOATING_IP_CREATED:
1061 case OPENSTACK_FLOATING_IP_UPDATED:
1062 case OPENSTACK_FLOATING_IP_REMOVED:
1063 case OPENSTACK_FLOATING_IP_ASSOCIATED:
1064 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
1065 default:
1066 // do nothing for the other events
1067 break;
1068 }
1069 }
Jian Li4d138702018-11-27 17:25:28 +09001070
1071 private void processRouterCreation(OpenstackRouterEvent event) {
1072 if (!isRelevantHelper()) {
1073 return;
1074 }
1075
1076 log.debug("Router(name:{}, ID:{}) is created",
1077 event.subject().getName(),
1078 event.subject().getId());
1079
1080 routerUpdated(event.subject());
1081 }
1082
1083 private void processRouterUpdate(OpenstackRouterEvent event) {
1084 if (!isRelevantHelper()) {
1085 return;
1086 }
1087
1088 log.debug("Router(name:{}, ID:{}) is updated",
1089 event.subject().getName(),
1090 event.subject().getId());
1091
1092 routerUpdated(event.subject());
1093 }
1094
1095 private void processRouterRemoval(OpenstackRouterEvent event) {
1096 if (!isRelevantHelper()) {
1097 return;
1098 }
1099
1100 log.debug("Router(name:{}, ID:{}) is removed",
1101 event.subject().getName(),
1102 event.subject().getId());
1103
1104 routerRemove(event.subject());
1105 }
1106
1107 private void processRouterIntfCreation(OpenstackRouterEvent event) {
1108 if (!isRelevantHelper()) {
1109 return;
1110 }
1111
1112 log.debug("Router interface {} added to router {}",
1113 event.routerIface().getPortId(),
1114 event.routerIface().getId());
1115
1116 routerIfaceAdded(event.subject(), event.routerIface());
1117 }
1118
1119 private void processRouterIntfUpdate(OpenstackRouterEvent event) {
1120 log.debug("Router interface {} on {} updated",
1121 event.routerIface().getPortId(),
1122 event.routerIface().getId());
1123 }
1124
1125 private void processRouterIntfRemoval(OpenstackRouterEvent event) {
1126 if (!isRelevantHelper()) {
1127 return;
1128 }
1129
1130 log.debug("Router interface {} removed from router {}",
1131 event.routerIface().getPortId(),
1132 event.routerIface().getId());
1133
1134 routerIfaceRemoved(event.subject(), event.routerIface());
1135 }
Hyunsun Moon44aac662017-02-18 02:07:01 +09001136 }
1137
1138 private class InternalNodeEventListener implements OpenstackNodeListener {
1139
Jian Li34220ea2018-11-14 01:30:24 +09001140 private boolean isRelevantHelper() {
1141 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
Hyunsun Moon44aac662017-02-18 02:07:01 +09001142 }
1143
1144 @Override
1145 public void event(OpenstackNodeEvent event) {
1146 OpenstackNode osNode = event.subject();
Hyunsun Moon44aac662017-02-18 02:07:01 +09001147 switch (event.type()) {
Hyunsun Moon0d457362017-06-27 17:19:41 +09001148 case OPENSTACK_NODE_COMPLETE:
1149 case OPENSTACK_NODE_INCOMPLETE:
Daniel Park6ed9cf02018-06-27 19:07:44 +09001150 case OPENSTACK_NODE_UPDATED:
1151 case OPENSTACK_NODE_REMOVED:
Hyunsun Moon44aac662017-02-18 02:07:01 +09001152 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +09001153 if (!isRelevantHelper()) {
1154 return;
1155 }
Jian Li4d138702018-11-27 17:25:28 +09001156 reconfigureRouters(osNode);
Hyunsun Moon44aac662017-02-18 02:07:01 +09001157 });
1158 break;
Hyunsun Moon0d457362017-06-27 17:19:41 +09001159 case OPENSTACK_NODE_CREATED:
Hyunsun Moon44aac662017-02-18 02:07:01 +09001160 default:
1161 break;
1162 }
1163 }
1164
Jian Li4d138702018-11-27 17:25:28 +09001165 private void reconfigureRouters(OpenstackNode osNode) {
Hyunsun Moon44aac662017-02-18 02:07:01 +09001166 osRouterService.routers().forEach(osRouter -> {
1167 routerUpdated(osRouter);
1168 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
1169 routerIfaceAdded(osRouter, iface);
1170 });
1171 });
Jian Li4d138702018-11-27 17:25:28 +09001172 log.info("Reconfigure routers for {}", osNode.hostname());
Hyunsun Moon44aac662017-02-18 02:07:01 +09001173 }
1174 }
sangho072c4dd2017-05-17 10:45:21 +09001175
1176 private class InternalInstancePortListener implements InstancePortListener {
1177
Jian Li34220ea2018-11-14 01:30:24 +09001178 private boolean isRelevantHelper(InstancePortEvent event) {
1179 return mastershipService.isLocalMaster(event.subject().deviceId());
sangho072c4dd2017-05-17 10:45:21 +09001180 }
1181
1182 @Override
1183 public void event(InstancePortEvent event) {
1184 InstancePort instPort = event.subject();
1185 switch (event.type()) {
sangho072c4dd2017-05-17 10:45:21 +09001186 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +09001187 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Li4d138702018-11-27 17:25:28 +09001188 eventExecutor.execute(() ->
1189 processInstancePortDetection(event, instPort));
sangho072c4dd2017-05-17 10:45:21 +09001190 break;
1191 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Li4d138702018-11-27 17:25:28 +09001192 eventExecutor.execute(() ->
1193 processInstancePortRemoval(event, instPort));
Jian Liec5c32b2018-07-13 14:28:58 +09001194 break;
1195 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
Jian Li4d138702018-11-27 17:25:28 +09001196 eventExecutor.execute(() ->
1197 processInstanceMigrationStart(event, instPort));
sangho072c4dd2017-05-17 10:45:21 +09001198 break;
Jian Li24ec59f2018-05-23 19:01:25 +09001199 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
Jian Li4d138702018-11-27 17:25:28 +09001200 eventExecutor.execute(() ->
1201 processInstanceMigrationEnd(event, instPort));
Jian Li24ec59f2018-05-23 19:01:25 +09001202 break;
sangho072c4dd2017-05-17 10:45:21 +09001203 default:
1204 break;
1205 }
1206 }
1207
Jian Li4d138702018-11-27 17:25:28 +09001208 private void processInstancePortDetection(InstancePortEvent event,
1209 InstancePort instPort) {
1210 if (!isRelevantHelper(event)) {
1211 return;
1212 }
1213
1214 log.info("RoutingHandler: Instance port detected MAC:{} IP:{}",
1215 instPort.macAddress(),
1216 instPort.ipAddress());
1217
1218 instPortDetected(event.subject());
1219 }
1220
1221 private void processInstancePortRemoval(InstancePortEvent event,
1222 InstancePort instPort) {
1223 if (!isRelevantHelper(event)) {
1224 return;
1225 }
1226
1227 log.info("RoutingHandler: Instance port vanished MAC:{} IP:{}",
1228 instPort.macAddress(),
1229 instPort.ipAddress());
1230
1231 instPortRemoved(event.subject());
1232 }
1233
1234 private void processInstanceMigrationStart(InstancePortEvent event,
1235 InstancePort instPort) {
1236 if (!isRelevantHelper(event)) {
1237 return;
1238 }
1239
1240 log.info("RoutingHandler: Migration started for MAC:{} IP:{}",
1241 instPort.macAddress(),
1242 instPort.ipAddress());
1243
1244 instPortDetected(instPort);
1245 }
1246
1247 private void processInstanceMigrationEnd(InstancePortEvent event,
1248 InstancePort instPort) {
1249 log.info("RoutingHandler: Migration finished for MAC:{} IP:{}",
1250 instPort.macAddress(),
1251 instPort.ipAddress());
1252 // TODO: need to reconfigure rules to point to update VM
1253 }
1254
sangho072c4dd2017-05-17 10:45:21 +09001255 private void instPortDetected(InstancePort instPort) {
Jian Li4d138702018-11-27 17:25:28 +09001256 Network network = osNetworkAdminService.network(instPort.networkId());
1257 if (network.getNetworkType() == FLAT) {
daniel park796c2eb2018-03-22 17:01:51 +09001258 return;
1259 }
Daniel Parkc64b4c62018-05-09 18:13:39 +09001260
1261 if (useStatefulSnat) {
1262 osNodeService.completeNodes(GATEWAY)
1263 .forEach(gwNode -> setRulesForSnatIngressRule(
1264 gwNode.intgBridge(),
Jian Li4d138702018-11-27 17:25:28 +09001265 Long.parseLong(network.getProviderSegID()),
1266 IpPrefix.valueOf(instPort.ipAddress(), VM_PREFIX),
Daniel Parkc64b4c62018-05-09 18:13:39 +09001267 instPort.deviceId(), true));
1268 }
sangho072c4dd2017-05-17 10:45:21 +09001269 }
1270
1271 private void instPortRemoved(InstancePort instPort) {
Jian Li4d138702018-11-27 17:25:28 +09001272 Network network = osNetworkAdminService.network(instPort.networkId());
1273 if (network.getNetworkType() == FLAT) {
daniel park796c2eb2018-03-22 17:01:51 +09001274 return;
1275 }
Daniel Parkc64b4c62018-05-09 18:13:39 +09001276
1277 if (useStatefulSnat) {
1278 osNodeService.completeNodes(GATEWAY)
1279 .forEach(gwNode -> setRulesForSnatIngressRule(
1280 gwNode.intgBridge(),
Jian Li4d138702018-11-27 17:25:28 +09001281 Long.parseLong(network.getProviderSegID()),
1282 IpPrefix.valueOf(instPort.ipAddress(), VM_PREFIX),
Daniel Parkc64b4c62018-05-09 18:13:39 +09001283 instPort.deviceId(), false));
1284 }
sangho072c4dd2017-05-17 10:45:21 +09001285 }
1286 }
Hyunsun Moon44aac662017-02-18 02:07:01 +09001287}