blob: 81748c6249adf344f829192b576907b92e6f7ea3 [file] [log] [blame]
Kalhee Kim495c9b22017-11-07 16:32:09 +00001/*
2 * Copyright 2017-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 *
16 */
17package org.onosproject.dhcprelay;
18
19import org.onlab.packet.BasePacket;
20import org.onlab.packet.DHCP6;
21import org.onlab.packet.DHCP6.MsgType;
22import org.onlab.packet.Ip6Address;
23import org.onlab.packet.IpAddress;
24import org.onlab.packet.VlanId;
25import org.onlab.packet.dhcp.Dhcp6RelayOption;
26import org.onlab.packet.dhcp.Dhcp6Option;
27
28import org.onlab.packet.Ethernet;
29import org.onlab.packet.IPv6;
30import org.onlab.packet.MacAddress;
31import org.onlab.packet.UDP;
32
33import org.onlab.util.HexString;
34import org.onosproject.dhcprelay.api.DhcpServerInfo;
Kalhee Kimd94ceea2017-11-29 19:03:02 +000035import org.onosproject.dhcprelay.store.DhcpRelayCounters;
Kalhee Kim495c9b22017-11-07 16:32:09 +000036import org.onosproject.net.ConnectPoint;
37import org.onosproject.net.host.InterfaceIpAddress;
38import org.onosproject.net.intf.Interface;
39import org.onosproject.net.packet.PacketContext;
40import org.onosproject.net.DeviceId;
41
42import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44import java.util.Set;
45import java.util.List;
46import java.util.ArrayList;
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080047import org.onosproject.net.intf.InterfaceService;
Kalhee Kim495c9b22017-11-07 16:32:09 +000048
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080049import org.onosproject.net.Host;
50import org.onosproject.net.host.HostService;
51import org.onosproject.net.HostLocation;
Kalhee Kim495c9b22017-11-07 16:32:09 +000052
53import static com.google.common.base.Preconditions.checkNotNull;
54
Taras Lemkin96a0d342018-03-26 14:52:58 +000055public final class Dhcp6HandlerUtil {
Kalhee Kim495c9b22017-11-07 16:32:09 +000056
Taras Lemkin96a0d342018-03-26 14:52:58 +000057 private static final Logger log = LoggerFactory.getLogger(Dhcp6HandlerUtil.class);
Kalhee Kim495c9b22017-11-07 16:32:09 +000058
Taras Lemkin96a0d342018-03-26 14:52:58 +000059 private Dhcp6HandlerUtil() {
60 }
Kalhee Kim495c9b22017-11-07 16:32:09 +000061
Kalhee Kim495c9b22017-11-07 16:32:09 +000062 // Returns the first v6 interface ip out of a set of interfaces or null.
63 // Checks all interfaces, and ignores v6 interface ips
Taras Lemkin96a0d342018-03-26 14:52:58 +000064 public static Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
Kalhee Kim495c9b22017-11-07 16:32:09 +000065 for (Interface intf : intfs) {
66 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
67 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
68 if (relayAgentIp != null) {
69 return relayAgentIp;
70 }
71 }
72 }
73 return null;
74 }
75
76 /**
Taras Lemkin96a0d342018-03-26 14:52:58 +000077 * Returns the first interface ip from interface.
78 *
79 * @param iface interface of one connect point
80 * @return the first interface IP; null if not exists an IP address in
81 * these interfaces
82 */
83 private static Ip6Address getFirstIpFromInterface(Interface iface) {
84 checkNotNull(iface, "Interface can't be null");
85 return iface.ipAddressesList().stream()
86 .map(InterfaceIpAddress::ipAddress)
87 .filter(IpAddress::isIp6)
88 .map(IpAddress::getIp6Address)
89 .findFirst()
90 .orElse(null);
91 }
92 /**
Lior Assoulinea21c0ca2018-01-28 16:18:48 -080093 *
94 * process the LQ reply packet from dhcp server.
95 *
96 * @param defaultServerInfoList default server list
97 * @param indirectServerInfoList default indirect server list
98 * @param serverInterface server interface
99 * @param interfaceService interface service
100 * @param hostService host service
101 * @param context packet context
102 * @param receivedPacket server ethernet packet
103 * @param recevingInterfaces set of server side interfaces
104 * @return a packet ready to be sent to relevant output interface
105 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000106 public static InternalPacket processLQ6PacketFromServer(
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800107 List<DhcpServerInfo> defaultServerInfoList,
108 List<DhcpServerInfo> indirectServerInfoList,
109 Interface serverInterface,
110 InterfaceService interfaceService,
111 HostService hostService,
112 PacketContext context,
113 Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
114 // get dhcp6 header.
115 Ethernet etherReply = (Ethernet) receivedPacket.clone();
116 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
117 UDP udpPacket = (UDP) ipv6Packet.getPayload();
118 DHCP6 lq6Reply = (DHCP6) udpPacket.getPayload();
119
120 // TODO: refactor
121 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
122 DeviceId receivedFromDevice = receivedFrom.deviceId();
123 DhcpServerInfo serverInfo;
124 Ip6Address dhcpServerIp = null;
125 ConnectPoint dhcpServerConnectPoint = null;
126 MacAddress dhcpConnectMac = null;
127 VlanId dhcpConnectVlan = null;
128 Ip6Address dhcpGatewayIp = null;
129
130 // todo: refactor
131 Ip6Address indirectDhcpServerIp = null;
132 ConnectPoint indirectDhcpServerConnectPoint = null;
133 MacAddress indirectDhcpConnectMac = null;
134 VlanId indirectDhcpConnectVlan = null;
135 Ip6Address indirectDhcpGatewayIp = null;
136 Ip6Address indirectRelayAgentIpFromCfg = null;
137
138 if (!defaultServerInfoList.isEmpty()) {
139 serverInfo = defaultServerInfoList.get(0);
140 dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
141 dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
142 dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
143 dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
144 dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
145 }
146
147 if (!indirectServerInfoList.isEmpty()) {
148 serverInfo = indirectServerInfoList.get(0);
149 indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
150 indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
151 indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
152 indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
153 indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
154 indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
155 }
156
157 Boolean directConnFlag = directlyConnected(lq6Reply);
158 ConnectPoint inPort = context.inPacket().receivedFrom();
Charles Chan3a15b282018-02-20 10:29:05 -0800159 if ((directConnFlag || indirectDhcpServerIp == null)
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800160 && !inPort.equals(dhcpServerConnectPoint)) {
161 log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
Taras Lemkin96a0d342018-03-26 14:52:58 +0000162 inPort, dhcpServerConnectPoint);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800163 return null;
164 }
165
166 if (!directConnFlag && indirectDhcpServerIp != null &&
167 !inPort.equals(indirectDhcpServerConnectPoint)) {
168 log.warn("Receiving port {} is not the same as server connect point {} for indirect",
Taras Lemkin96a0d342018-03-26 14:52:58 +0000169 inPort, indirectDhcpServerConnectPoint);
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800170 return null;
171 }
172
173
174 Ip6Address nextHopIP = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
175 // use hosts store to find out the next hop mac and connection point
176 Set<Host> hosts = hostService.getHostsByIp(nextHopIP);
177 Host host;
178 if (!hosts.isEmpty()) {
179 host = hosts.iterator().next();
180 } else {
181 log.warn("Host {} is not in store", nextHopIP);
182 return null;
183 }
184
185 HostLocation hl = host.location();
186 String clientConnectionPointStr = hl.toString(); // iterator().next());
187 ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
188
189
190 VlanId originalPacketVlanId = VlanId.vlanId(etherReply.getVlanID());
191 Interface iface;
192 iface = interfaceService.getInterfacesByPort(clientConnectionPoint)
193 .stream()
194 .filter(iface1 -> interfaceContainsVlan(iface1, originalPacketVlanId))
195 .findFirst()
196 .orElse(null);
197
198 etherReply.setSourceMACAddress(iface.mac());
199 etherReply.setDestinationMACAddress(host.mac());
200
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800201 // workaround for a bug where core sends src port as 547 (server)
202 udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
203 udpPacket.setPayload(lq6Reply);
204 udpPacket.resetChecksum();
205 ipv6Packet.setPayload(udpPacket);
206 etherReply.setPayload(ipv6Packet);
207
Taras Lemkin96a0d342018-03-26 14:52:58 +0000208 return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
Kalhee Kim495c9b22017-11-07 16:32:09 +0000209 }
210
211 /**
212 * extract DHCP6 payload from dhcp6 relay message within relay-forwrd/reply.
213 *
Sangyeok Sim61ac5702018-06-13 13:33:51 +0900214 * @param dhcp6 dhcp6 relay-reply or relay-forward
Kalhee Kim495c9b22017-11-07 16:32:09 +0000215 * @return dhcp6Packet dhcp6 packet extracted from relay-message
216 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000217 public static DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000218
219 // extract the relay message if exist
220 DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
221 .filter(opt -> opt instanceof Dhcp6RelayOption)
222 .map(BasePacket::getPayload)
223 .map(pld -> (DHCP6) pld)
224 .findFirst()
225 .orElse(null);
226 if (dhcp6Payload == null) {
227 // Can't find dhcp payload
228 log.debug("Can't find dhcp6 payload from relay message");
229 } else {
230 log.debug("dhcp6 payload found from relay message {}", dhcp6Payload);
231 }
232 return dhcp6Payload;
233 }
234
235 /**
236 * find the leaf DHCP6 packet from multi-level relay packet.
237 *
238 * @param relayPacket dhcp6 relay packet
239 * @return leafPacket non-relay dhcp6 packet
240 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000241 public static DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000242 DHCP6 dhcp6Parent = relayPacket;
243 DHCP6 dhcp6Child = null;
244
245 log.debug("getDhcp6Leaf entered.");
246 while (dhcp6Parent != null) {
247 dhcp6Child = dhcp6PacketFromRelayPacket(dhcp6Parent);
248 if (dhcp6Child != null) {
249 if (dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
250 dhcp6Child.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
251 log.debug("leaf dhcp6 packet found.");
252 break;
253 } else {
254 // found another relay, go for another loop
255 dhcp6Parent = dhcp6Child;
256 }
257 } else {
258 log.debug("Expected dhcp6 within relay pkt, but no dhcp6 leaf found.");
259 break;
260 }
261 }
262 return dhcp6Child;
263 }
264
265 /**
Taras Lemkin96a0d342018-03-26 14:52:58 +0000266 * Determine DHCP message type (direct DHCPv6 or wrapped into relay messages).
267 *
268 * @param relayPacket {@link DHCP6} packet to be parsed
269 * @return {@link DHCP6.MsgType} contained message type of dhcpv6 packet/relay-message
270 */
271 public static DHCP6.MsgType getDhcp6LeafMessageType(DHCP6 relayPacket) {
272 checkNotNull(relayPacket);
273 DHCP6 dhcp6Child = getDhcp6Leaf(relayPacket);
274 return DHCP6.MsgType.getType(dhcp6Child != null ? dhcp6Child.getMsgType() : relayPacket.getMsgType());
275 }
276
277 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000278 * check if DHCP6 relay-reply is reply.
279 *
280 * @param relayPacket dhcp6 relay-reply
281 * @return boolean relay-reply contains ack
282 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000283 public static boolean isDhcp6Reply(DHCP6 relayPacket) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000284 DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
285 if (leafDhcp6 != null) {
286 if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
287 log.debug("isDhcp6Reply true.");
288 return true; // must be directly connected
289 } else {
290 log.debug("isDhcp6Reply false. leaf dhcp6 is not replay. MsgType {}", leafDhcp6.getMsgType());
291 }
292 } else {
293 log.debug("isDhcp6Reply false. Expected dhcp6 within relay pkt but not found.");
294 }
295 log.debug("isDhcp6Reply false.");
296 return false;
297 }
298
299 /**
300 * check if DHCP6 is release or relay-forward contains release.
301 *
302 * @param dhcp6Payload dhcp6 packet
303 * @return boolean dhcp6 contains release
304 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000305 public static boolean isDhcp6Release(DHCP6 dhcp6Payload) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000306 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
307 log.debug("isDhcp6Release true.");
308 return true; // must be directly connected
309 } else {
310 DHCP6 dhcp6Leaf = getDhcp6Leaf(dhcp6Payload);
311 if (dhcp6Leaf != null) {
312 if (dhcp6Leaf.getMsgType() == DHCP6.MsgType.RELEASE.value()) {
313 log.debug("isDhcp6Release true. indirectlry connected");
314 return true;
315 } else {
316 log.debug("leaf dhcp6 is not release. MsgType {}", dhcp6Leaf.getMsgType());
317 return false;
318 }
319 } else {
320 log.debug("isDhcp6Release false. dhcp6 is niether relay nor release.");
321 return false;
322 }
323 }
324 }
325
326
327 /**
328 * convert dhcp6 msgType to String.
329 *
330 * @param msgTypeVal msgType byte of dhcp6 packet
331 * @return String string value of dhcp6 msg type
332 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000333 public static String getMsgTypeStr(byte msgTypeVal) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000334 MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
335 return DHCP6.MsgType.getMsgTypeStr(msgType);
336 }
337
338 /**
339 * find the string of dhcp6 leaf packets's msg type.
340 *
341 * @param directConnFlag boolean value indicating direct/indirect connection
342 * @param dhcp6Packet dhcp6 packet
343 * @return String string value of dhcp6 leaf packet msg type
344 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000345 public static String findLeafMsgType(boolean directConnFlag, DHCP6 dhcp6Packet) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000346 if (directConnFlag) {
347 return getMsgTypeStr(dhcp6Packet.getMsgType());
348 } else {
349 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
350 if (leafDhcp != null) {
351 return getMsgTypeStr(leafDhcp.getMsgType());
352 } else {
Kalhee Kimd94ceea2017-11-29 19:03:02 +0000353 return DhcpRelayCounters.INVALID_PACKET;
Kalhee Kim495c9b22017-11-07 16:32:09 +0000354 }
355 }
356 }
357
358 /**
359 * Determind if an Interface contains a vlan id.
360 *
361 * @param iface the Interface
362 * @param vlanId the vlan id
363 * @return true if the Interface contains the vlan id
364 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000365 public static boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000366 if (vlanId.equals(VlanId.NONE)) {
367 // untagged packet, check if vlan untagged or vlan native is not NONE
368 return !iface.vlanUntagged().equals(VlanId.NONE) ||
369 !iface.vlanNative().equals(VlanId.NONE);
370 }
371 // tagged packet, check if the interface contains the vlan
372 return iface.vlanTagged().contains(vlanId);
373 }
374
375 /**
Kalhee Kim495c9b22017-11-07 16:32:09 +0000376 * Check if the host is directly connected to the network or not.
377 *
378 * @param dhcp6Payload the dhcp6 payload
379 * @return true if the host is directly connected to the network; false otherwise
380 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000381 public static boolean directlyConnected(DHCP6 dhcp6Payload) {
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800382
Kalhee Kim495c9b22017-11-07 16:32:09 +0000383 log.debug("directlyConnected enters");
Lior Assoulinea21c0ca2018-01-28 16:18:48 -0800384 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.LEASEQUERY.value() ||
385 dhcp6Payload.getMsgType() == DHCP6.MsgType.LEASEQUERY_REPLY.value()) {
386 log.debug("directlyConnected false. MsgType {}", dhcp6Payload.getMsgType());
387
388 return false;
389 }
Kalhee Kim495c9b22017-11-07 16:32:09 +0000390
391 if (dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_FORW.value() &&
392 dhcp6Payload.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
393 log.debug("directlyConnected true. MsgType {}", dhcp6Payload.getMsgType());
394
395 return true;
396 }
397 // Regardless of relay-forward or relay-replay, check if we see another relay message
398 DHCP6 dhcp6Payload2 = dhcp6PacketFromRelayPacket(dhcp6Payload);
399 if (dhcp6Payload2 != null) {
400 if (dhcp6Payload.getMsgType() == DHCP6.MsgType.RELAY_FORW.value()) {
Sangyeok Sim61ac5702018-06-13 13:33:51 +0900401 log.debug("directlyConnected false. 1st relay-forward, 2nd MsgType {}", dhcp6Payload2.getMsgType());
Kalhee Kim495c9b22017-11-07 16:32:09 +0000402 return false;
403 } else {
404 // relay-reply
Taras Lemkin96a0d342018-03-26 14:52:58 +0000405 if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()
406 && dhcp6Payload2.getMsgType() != MsgType.LEASEQUERY_REPLY.value()) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000407 log.debug("directlyConnected true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
408 return true; // must be directly connected
409 } else {
410 log.debug("directlyConnected false. 1st relay-reply, 2nd relay-reply MsgType {}",
411 dhcp6Payload2.getMsgType());
412 return false; // must be indirectly connected
413 }
414 }
415 } else {
416 log.debug("directlyConnected true.");
417 return true;
418 }
419 }
420 /**
421 * Check if a given server info has v6 ipaddress.
422 *
423 * @param serverInfo server info to check
424 * @return true if server info has v6 ip address; false otherwise
425 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000426 public static boolean isServerIpEmpty(DhcpServerInfo serverInfo) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000427 if (!serverInfo.getDhcpServerIp6().isPresent()) {
428 log.warn("DhcpServerIp not available, use default DhcpServerIp {}",
429 HexString.toHexString(serverInfo.getDhcpServerIp6().get().toOctets()));
430 return true;
431 }
432 return false;
433 }
434
Taras Lemkin96a0d342018-03-26 14:52:58 +0000435 private static boolean isConnectMacEmpty(DhcpServerInfo serverInfo, Set<Interface> clientInterfaces) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000436 if (!serverInfo.getDhcpConnectMac().isPresent()) {
437 log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
438 + "packet processing from client on port: {}",
439 !serverInfo.getDhcpGatewayIp6().isPresent() ? "server IP " + serverInfo.getDhcpServerIp6()
440 : "gateway IP " + serverInfo.getDhcpGatewayIp6(),
441 clientInterfaces.iterator().next().connectPoint());
442 return true;
443 }
444 return false;
445 }
446
Taras Lemkin96a0d342018-03-26 14:52:58 +0000447 private static boolean isRelayAgentIpFromCfgEmpty(DhcpServerInfo serverInfo, DeviceId receivedFromDevice) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000448 if (!serverInfo.getRelayAgentIp6(receivedFromDevice).isPresent()) {
449 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic.");
450 return true;
451 }
452 return false;
453 }
454
Taras Lemkin96a0d342018-03-26 14:52:58 +0000455 private static Dhcp6Option getInterfaceIdIdOption(PacketContext context, Ethernet clientPacket) {
Kalhee Kim495c9b22017-11-07 16:32:09 +0000456 String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
457 Dhcp6Option interfaceId = new Dhcp6Option();
458 interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
459 byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
460 byte[] inPortStringBytes = inPortString.getBytes();
461 byte[] vlanIdBytes = new byte[2];
462 vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
463 vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
464 byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
465 inPortStringBytes.length + vlanIdBytes.length];
466 log.debug("Length: interfaceIdBytes {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
467 interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
468 vlanIdBytes.length);
469
470 System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
471 System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length,
472 inPortStringBytes.length);
473 System.arraycopy(vlanIdBytes, 0, interfaceIdBytes,
474 clientSoureMacBytes.length + inPortStringBytes.length,
475 vlanIdBytes.length);
476 interfaceId.setData(interfaceIdBytes);
477 interfaceId.setLength((short) interfaceIdBytes.length);
478 log.debug("interfaceId write srcMac {} portString {}",
479 HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
480 return interfaceId;
481 }
482
Taras Lemkin96a0d342018-03-26 14:52:58 +0000483 private static void addDhcp6OptionsFromClient(List<Dhcp6Option> options, byte[] dhcp6PacketByte,
Kalhee Kim495c9b22017-11-07 16:32:09 +0000484 PacketContext context, Ethernet clientPacket) {
485 Dhcp6Option relayMessage = new Dhcp6Option();
486 relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
487 relayMessage.setLength((short) dhcp6PacketByte.length);
488 relayMessage.setData(dhcp6PacketByte);
489 options.add(relayMessage);
490 // create interfaceId option
491 Dhcp6Option interfaceId = getInterfaceIdIdOption(context, clientPacket);
492 options.add(interfaceId);
493 }
494
495 /**
496 * build the DHCP6 solicit/request packet with gatewayip.
497 *
498 * @param context packet context
499 * @param clientPacket client ethernet packet
500 * @param clientInterfaces set of client side interfaces
501 * @param serverInfo target server which a packet is generated for
502 * @param serverInterface target server interface
503 * @return ethernet packet with dhcp6 packet info
504 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000505 public static Ethernet buildDhcp6PacketFromClient(PacketContext context, Ethernet clientPacket,
Kalhee Kim495c9b22017-11-07 16:32:09 +0000506 Set<Interface> clientInterfaces, DhcpServerInfo serverInfo,
507 Interface serverInterface) {
508 ConnectPoint receivedFrom = context.inPacket().receivedFrom();
509 DeviceId receivedFromDevice = receivedFrom.deviceId();
510
511 Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
512 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
513 if (relayAgentIp == null || relayAgentMac == null) {
514 log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
515 + "packet from client on port: {}. Aborting packet processing",
516 clientInterfaces.iterator().next().connectPoint());
517 return null;
518 }
519 IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
520 UDP clientUdp = (UDP) clientIpv6.getPayload();
521 DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
522 boolean directConnFlag = directlyConnected(clientDhcp6);
523
524 Ip6Address serverIpFacing = getFirstIpFromInterface(serverInterface);
525 if (serverIpFacing == null || serverInterface.mac() == null) {
526 log.warn("No IP v6 address for server Interface {}", serverInterface);
527 return null;
528 }
529
530 Ethernet etherReply = clientPacket.duplicate();
531 etherReply.setSourceMACAddress(serverInterface.mac());
532
533 // set default info and replace with indirect if available later on.
534 if (serverInfo.getDhcpConnectMac().isPresent()) {
535 etherReply.setDestinationMACAddress(serverInfo.getDhcpConnectMac().get());
536 }
537 if (serverInfo.getDhcpConnectVlan().isPresent()) {
538 etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
539 }
540 IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
541 byte[] peerAddress = clientIpv6.getSourceAddress();
542 ipv6Packet.setSourceAddress(serverIpFacing.toOctets());
543 ipv6Packet.setDestinationAddress(serverInfo.getDhcpServerIp6().get().toOctets());
544 UDP udpPacket = (UDP) ipv6Packet.getPayload();
545 udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
546 DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
547 byte[] dhcp6PacketByte = dhcp6Packet.serialize();
548
549 DHCP6 dhcp6Relay = new DHCP6();
550
551 dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
552
553 if (directConnFlag) {
554 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
555 } else {
556 if (isServerIpEmpty(serverInfo)) {
557 log.warn("indirect DhcpServerIp empty... use default server ");
558 } else {
559 // Indirect case, replace destination to indirect dhcp server if exist
560 // Check if mac is obtained for valid server ip
561 if (isConnectMacEmpty(serverInfo, clientInterfaces)) {
562 log.warn("indirect Dhcp ConnectMac empty ...");
563 return null;
564 }
565 etherReply.setDestinationMACAddress(serverInfo.getDhcpConnectMac().get());
566 etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
567 ipv6Packet.setDestinationAddress(serverInfo.getDhcpServerIp6().get().toOctets());
568 }
569 if (isRelayAgentIpFromCfgEmpty(serverInfo, receivedFromDevice)) {
570 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
571 log.debug("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
572 HexString.toHexString(relayAgentIp.toOctets(), ":"));
573 } else {
574 dhcp6Relay.setLinkAddress(serverInfo.getRelayAgentIp6(receivedFromDevice).get().toOctets());
575 }
576 }
577 // peer address: address of the client or relay agent from which the message to be relayed was received.
578 dhcp6Relay.setPeerAddress(peerAddress);
579 // directly connected case, hop count is zero; otherwise, hop count + 1
580 if (directConnFlag) {
581 dhcp6Relay.setHopCount((byte) 0);
582 } else {
583 dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
584 }
585
586 List<Dhcp6Option> options = new ArrayList<>();
587 addDhcp6OptionsFromClient(options, dhcp6PacketByte, context, clientPacket);
588 dhcp6Relay.setOptions(options);
589 udpPacket.setPayload(dhcp6Relay);
590 udpPacket.resetChecksum();
591 ipv6Packet.setPayload(udpPacket);
592 ipv6Packet.setHopLimit((byte) 64);
593 etherReply.setPayload(ipv6Packet);
594
595 return etherReply;
596 }
597
598 /**
599 * build the DHCP6 solicit/request packet with gatewayip.
600 *
601 * @param directConnFlag flag indicating if packet is from direct client or not
602 * @param serverInfo server to check its connect point
603 * @return boolean true if serverInfo is found; false otherwise
604 */
Taras Lemkin96a0d342018-03-26 14:52:58 +0000605 public static boolean checkDhcpServerConnPt(boolean directConnFlag,
Kalhee Kim495c9b22017-11-07 16:32:09 +0000606 DhcpServerInfo serverInfo) {
607 if (serverInfo.getDhcpServerConnectPoint() == null) {
608 log.warn("DHCP6 server connect point for {} connPt {}",
609 directConnFlag ? "direct" : "indirect", serverInfo.getDhcpServerConnectPoint());
610 return false;
611 }
612 return true;
613 }
614}