summaryrefslogtreecommitdiffstats
path: root/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java
blob: eba7a1f85d25b99554a414d0cb6771754f290836 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.fd.honeycomb.translate.v3po.interfaces.acl;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import io.fd.honeycomb.translate.util.RWUtils;
import io.fd.honeycomb.translate.v3po.util.JvppReplyConsumer;
import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collector;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
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;
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.packet.handling.Permit;
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;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.openvpp.jvpp.VppBaseCallException;
import org.openvpp.jvpp.core.dto.ClassifyAddDelSession;
import org.openvpp.jvpp.core.dto.ClassifyAddDelSessionReply;
import org.openvpp.jvpp.core.dto.ClassifyAddDelTable;
import org.openvpp.jvpp.core.dto.ClassifyAddDelTableReply;
import org.openvpp.jvpp.core.dto.InputAclSetInterface;
import org.openvpp.jvpp.core.future.FutureJVppCore;

/**
 * Base writer for translation of ietf-acl model ACEs to VPP's classify tables and sessions.
 * <p/>
 * Creates one classify table with single session per ACE.
 *
 * @param <T> type of access control list entry
 */
abstract class AbstractAceWriter<T extends AceType> implements AceWriter, JvppReplyConsumer {

    // TODO: HONEYCOMB-181 minimise memory used by classify tables (we create a lot of them to make ietf-acl model
    // mapping more convenient):
    // according to https://wiki.fd.io/view/VPP/Introduction_To_N-tuple_Classifiers#Creating_a_classifier_table,
    // classify table needs 16*(1 + match_n_vectors) bytes, but this does not quite work, so setting 8K for now
    protected static final int TABLE_MEM_SIZE = 8 * 1024;

    @VisibleForTesting
    static final int VLAN_TAG_LEN = 4;

    private static final Collector<PacketHandling, ?, PacketHandling> SINGLE_ITEM_COLLECTOR =
            RWUtils.singleItemCollector();

    private final FutureJVppCore futureJVppCore;

    public AbstractAceWriter(@Nonnull final FutureJVppCore futureJVppCore) {
        this.futureJVppCore = checkNotNull(futureJVppCore, "futureJVppCore should not be null");
    }

    /**
     * Creates classify table for given ACE.
     *
     * @param action         packet handling action (permit/deny)
     * @param ace            ACE to be translated
     * @param nextTableIndex classify table index
     * @param vlanTags       number of vlan tags
     * @return classify table that represents given ACE
     */
    protected abstract ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action,
                                                               @Nonnull final T ace,
                                                               final int nextTableIndex,
                                                               final int vlanTags);

    /**
     * Creates classify session for given ACE.
     *
     * @param action     packet handling action (permit/deny)
     * @param ace        ACE to be translated
     * @param tableIndex classify table index for the given session
     * @param vlanTags   number of vlan tags
     * @return classify session that represents given ACE
     */
    protected abstract ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action,
                                                                   @Nonnull final T ace,
                                                                   final int tableIndex,
                                                                   final int vlanTags);

    /**
     * Sets classify table index for input_acl_set_interface request.
     *
     * @param request    request DTO
     * @param tableIndex pointer to a chain of classify tables
     */
    protected abstract void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex);

    @Override
    public final void write(@Nonnull final InstanceIdentifier<?> id, @Nonnull final List<Ace> aces,
                            @Nonnull final InputAclSetInterface request, @Nonnegative final int vlanTags)
            throws VppBaseCallException, WriteTimeoutException {
        final PacketHandling action = aces.stream().map(ace -> ace.getActions().getPacketHandling()).distinct()
                .collect(SINGLE_ITEM_COLLECTOR);

        checkArgument(vlanTags >= 0 && vlanTags <= 2, "Number of vlan tags %s is not in [0,2] range");

        int nextTableIndex = -1;
        for (final Ace ace : aces) {
            // Create table + session per entry

            final ClassifyAddDelTable ctRequest =
                    createClassifyTable(action, (T) ace.getMatches().getAceType(), nextTableIndex, vlanTags);
            nextTableIndex = createClassifyTable(id, ctRequest);
            createClassifySession(id,
                    createClassifySession(action, (T) ace.getMatches().getAceType(), nextTableIndex, vlanTags));
        }
        setClassifyTable(request, nextTableIndex);
    }

    private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id,
                                    @Nonnull final ClassifyAddDelTable request)
            throws VppBaseCallException, WriteTimeoutException {
        final CompletionStage<ClassifyAddDelTableReply> cs = futureJVppCore.classifyAddDelTable(request);

        final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id);
        return reply.newTableIndex;
    }

    private void createClassifySession(@Nonnull final InstanceIdentifier<?> id,
                                       @Nonnull final ClassifyAddDelSession request)
            throws VppBaseCallException, WriteTimeoutException {
        final CompletionStage<ClassifyAddDelSessionReply> cs = futureJVppCore.classifyAddDelSession(request);

        getReplyForWrite(cs.toCompletableFuture(), id);
    }

    protected ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action, final int nextTableIndex) {
        final ClassifyAddDelTable request = new ClassifyAddDelTable();
        request.isAdd = 1;
        request.tableIndex = -1; // value not present

        request.nbuckets = 1; // we expect exactly one session per table

        if (action instanceof Permit) {
            request.missNextIndex = 0; // for list of permit rules, deny (0) should be default action
        } else { // deny is default value
            request.missNextIndex = -1; // for list of deny rules, permit (-1) should be default action
        }

        request.nextTableIndex = nextTableIndex;
        request.memorySize = TABLE_MEM_SIZE;

        return request;
    }

    protected ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, final int tableIndex) {
        final ClassifyAddDelSession request = new ClassifyAddDelSession();
        request.isAdd = 1;
        request.tableIndex = tableIndex;
        request.opaqueIndex = ~0; // value not used

        if (action instanceof Permit) {
            request.hitNextIndex = -1;
        } // deny (0) is default value

        return request;
    }

    protected int getVlanTagsLen(final int vlanTags) {
        return vlanTags * VLAN_TAG_LEN;
    }
}