2 * Copyright (c) 2016 Cisco and/or its affiliates.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress;
19 import static com.google.common.base.Preconditions.checkArgument;
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
24 import io.fd.honeycomb.translate.write.WriteContext;
25 import io.fd.honeycomb.translate.write.WriteFailedException;
26 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
27 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
28 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
29 import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
30 import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
31 import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
32 import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
33 import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
34 import io.fd.vpp.jvpp.core.future.FutureJVppCore;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.ListIterator;
39 import java.util.concurrent.CompletionStage;
40 import java.util.function.Predicate;
41 import java.util.stream.Collectors;
42 import java.util.stream.Stream;
43 import javax.annotation.Nonnegative;
44 import javax.annotation.Nonnull;
45 import javax.annotation.Nullable;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
65 public final class IetfAclWriter implements JvppReplyConsumer, AclTranslator {
67 private static final Logger LOG = LoggerFactory.getLogger(IetfAclWriter.class);
68 private static final int NOT_DEFINED = -1;
69 private final FutureJVppCore jvpp;
71 private Map<AclType, AceWriter<? extends AceType>> aceWriters = new HashMap<>();
73 public IetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore) {
74 this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null");
75 aceWriters.put(AclType.ETH, new AceEthWriter());
76 aceWriters.put(AclType.IP4, new AceIp4Writer());
77 aceWriters.put(AclType.IP6, new AceIp6Writer());
78 aceWriters.put(AclType.ETH_AND_IP, new AceIpAndEthWriter());
81 private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl,
82 @Nonnull final WriteContext writeContext) {
83 final String aclName = assignedAcl.getName();
84 final Class<? extends AclBase> aclType = assignedAcl.getType();
86 // ietf-acl updates are handled first, so we use writeContext.readAfter
87 final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl>
88 aclOptional = writeContext.readAfter(io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child(
89 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class,
90 new AclKey(aclName, aclType)));
91 checkArgument(aclOptional.isPresent(), "Acl lists not configured");
92 final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl
93 acl = aclOptional.get();
95 final AccessListEntries accessListEntries = acl.getAccessListEntries();
96 checkArgument(accessListEntries != null, "access list entries not configured");
98 return accessListEntries.getAce().stream();
101 void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex)
102 throws WriteFailedException {
103 final ClassifyTableByInterface request = new ClassifyTableByInterface();
104 request.swIfIndex = swIfIndex;
106 final CompletionStage<ClassifyTableByInterfaceReply> cs = jvpp.classifyTableByInterface(request);
107 final ClassifyTableByInterfaceReply reply = getReplyForDelete(cs.toCompletableFuture(), id);
109 // We unassign and remove all ACL-related classify tables for given interface (we assume we are the only
110 // classify table manager)
112 unassignClassifyTables(id, reply);
114 removeClassifyTable(id, reply.l2TableId);
115 removeClassifyTable(id, reply.ip4TableId);
116 removeClassifyTable(id, reply.ip6TableId);
119 private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id,
120 final ClassifyTableByInterfaceReply currentState)
121 throws WriteFailedException {
122 final InputAclSetInterface request = new InputAclSetInterface();
124 request.swIfIndex = currentState.swIfIndex;
125 request.l2TableIndex = currentState.l2TableId;
126 request.ip4TableIndex = currentState.ip4TableId;
127 request.ip6TableIndex = currentState.ip6TableId;
128 final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
129 jvpp.inputAclSetInterface(request);
130 getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
133 private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex)
134 throws WriteFailedException {
136 if (tableIndex == -1) {
137 return; // classify table id is absent
139 final ClassifyAddDelTable request = new ClassifyAddDelTable();
140 request.tableIndex = tableIndex;
141 final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
142 getReplyForDelete(cs.toCompletableFuture(), id);
145 void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, @Nonnull final List<Acl> acls,
146 final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode,
147 @Nonnull final WriteContext writeContext)
148 throws WriteFailedException {
149 write(id, swIfIndex, mode, acls, defaultAction, writeContext, 0);
152 private static boolean appliesToIp4Path(final Ace ace) {
153 final AceType aceType = ace.getMatches().getAceType();
154 if (aceType instanceof AceIp && ((AceIp) aceType).getAceIpVersion() instanceof AceIpv4) {
157 if (aceType instanceof AceEth) {
158 return true; // L2 only rules are possible for IP4 traffic
160 if (aceType instanceof AceIpAndEth && ((AceIpAndEth)aceType).getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4) {
166 private static boolean appliesToIp6Path(final Ace ace) {
167 final AceType aceType = ace.getMatches().getAceType();
168 if (aceType instanceof AceIp && ((AceIp) aceType).getAceIpVersion() instanceof AceIpv6) {
171 if (aceType instanceof AceEth) {
172 return true; // L2 only rules are possible for IP6 traffic
174 if (aceType instanceof AceIpAndEth && ((AceIpAndEth)aceType).getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6) {
180 void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, final InterfaceMode mode,
181 @Nonnull final List<Acl> acls, final AccessLists.DefaultAction defaultAction,
182 @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags)
183 throws WriteFailedException {
184 checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range");
186 final InputAclSetInterface request = new InputAclSetInterface();
188 request.swIfIndex = swIfIndex;
189 request.l2TableIndex = NOT_DEFINED;
190 request.ip4TableIndex = NOT_DEFINED;
191 request.ip6TableIndex = NOT_DEFINED;
193 if (InterfaceMode.L2.equals(mode)) {
194 final List<Ace> aces = getACEs(acls, writeContext, ace -> true);
195 request.l2TableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags);
197 final List<Ace> ip4Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp4Path));
198 request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags);
199 final List<Ace> ip6Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp6Path));
200 request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags);
203 final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
204 jvpp.inputAclSetInterface(request);
205 getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
208 private static List<Ace> getACEs(@Nonnull final List<Acl> acls, @Nonnull final WriteContext writeContext,
209 final Predicate<? super Ace> filter) {
210 return acls.stream().flatMap(acl -> aclToAceStream(acl, writeContext)).filter(filter)
211 .collect(Collectors.toList());
214 private int writeAces(final InstanceIdentifier<?> id, final List<Ace> aces,
215 final AccessLists.DefaultAction defaultAction, final InterfaceMode mode,
216 final int vlanTags) throws WriteFailedException {
217 if (aces.isEmpty()) {
221 int nextTableIndex = configureDefaultAction(id, defaultAction);
222 final ListIterator<Ace> iterator = aces.listIterator(aces.size());
223 while (iterator.hasPrevious()) {
224 final Ace ace = iterator.previous();
225 LOG.trace("Processing ACE: {}", ace);
227 final AceWriter aceWriter =
228 aceWriters.get(AclType.fromAce(ace));
229 if (aceWriter == null) {
230 LOG.warn("AceProcessor for {} not registered. Skipping ACE.", ace.getClass());
232 final AceType aceType = ace.getMatches().getAceType();
233 final PacketHandling action = ace.getActions().getPacketHandling();
234 final ClassifyAddDelTable ctRequest = aceWriter.createTable(aceType, mode, nextTableIndex, vlanTags);
235 nextTableIndex = createClassifyTable(id, ctRequest);
236 final ClassifyAddDelSession csRequest =
237 aceWriter.createSession(action, aceType, mode, nextTableIndex, vlanTags);
238 createClassifySession(id, csRequest);
241 return nextTableIndex;
244 private int configureDefaultAction(@Nonnull final InstanceIdentifier<?> id,
245 final AccessLists.DefaultAction defaultAction)
246 throws WriteFailedException {
247 ClassifyAddDelTable ctRequest = createTable(-1);
248 if (AccessLists.DefaultAction.Permit.equals(defaultAction)) {
249 ctRequest.missNextIndex = -1;
251 ctRequest.missNextIndex = 0;
253 ctRequest.mask = new byte[16];
254 ctRequest.skipNVectors = 0;
255 ctRequest.matchNVectors = 1;
256 return createClassifyTable(id, ctRequest);
259 private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id,
260 @Nonnull final ClassifyAddDelTable request)
261 throws WriteFailedException {
262 final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
264 final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id);
265 return reply.newTableIndex;
268 private void createClassifySession(@Nonnull final InstanceIdentifier<?> id,
269 @Nonnull final ClassifyAddDelSession request)
270 throws WriteFailedException {
271 final CompletionStage<ClassifyAddDelSessionReply> cs = jvpp.classifyAddDelSession(request);
273 getReplyForWrite(cs.toCompletableFuture(), id);
276 private enum AclType {
277 ETH, IP4, IP6, ETH_AND_IP;
280 private static AclType fromAce(final Ace ace) {
281 AclType result = null;
282 final AceType aceType;
284 aceType = ace.getMatches().getAceType();
285 if (aceType instanceof AceEth) {
287 } else if (aceType instanceof AceIp) {
288 final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion();
289 if (aceIpVersion instanceof AceIpv4) {
294 } else if (aceType instanceof AceIpAndEth) {
297 } catch (NullPointerException e) {
298 throw new IllegalArgumentException("Incomplete ACE: " + ace, e);
300 if (result == null) {
301 throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType));