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