xref: /dragonfly/sys/netproto/mpls/mpls_output.c (revision c443c74f42cc6a7f2385d56cc2b07c21939330d1)
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  * 3. Neither the name of The DragonFly Project nor the names of its
15  *    contributors may be used to endorse or promote products derived
16  *    from this software without specific, prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/malloc.h>       /* for M_NOWAIT */
34 #include <sys/mbuf.h>
35 #include <sys/systm.h>
36 
37 #include <net/if_var.h>
38 
39 #include <netinet/ip.h>
40 
41 #include <netproto/mpls/mpls.h>
42 #include <netproto/mpls/mpls_var.h>
43 
44 static int mpls_push(struct mbuf **, mpls_label_t,
45                          mpls_s_t, mpls_exp_t, mpls_ttl_t);
46 static int mpls_swap(struct mbuf *, mpls_label_t);
47 static int mpls_pop(struct mbuf *, mpls_s_t *);
48 
49 int
mpls_output(struct mbuf * m,struct rtentry * rt)50 mpls_output(struct mbuf *m, struct rtentry *rt)
51 {
52           struct sockaddr_mpls *smpls = NULL;
53           int error = 0, i;
54           mpls_s_t stackempty;
55           mpls_ttl_t ttl = 255;
56           struct ip *ip;
57 
58           M_ASSERTPKTHDR(m);
59 
60           /*
61            * Check if we are coming from an MPLS routing table lookup.
62            * The rt_key of this rtentry will have a family AF_MPLS if so.
63            */
64           stackempty = rt_key(rt)->sa_family != AF_MPLS ? 1 : 0;
65           if (stackempty) {
66                     switch (rt_key(rt)->sa_family) {
67                     case AF_INET:
68                               ip = mtod(m, struct ip *);
69                               ttl = ip->ip_ttl;
70                               break;
71                     }
72           }
73 
74           for (i=0; i < MPLS_MAXLOPS && rt->rt_shim[i] != NULL; ++i) {
75                     smpls = (struct sockaddr_mpls *)rt->rt_shim[i];
76                     switch (smpls->smpls_op) {
77                     case MPLSLOP_PUSH:
78                               error = mpls_push(&m,
79                                           ntohl(smpls->smpls_label),
80                                           /*
81                                            * If we are the first label push, then
82                                            * set the bottom-of-stack bit.
83                                            */
84                                           (stackempty && i == 0) ? 1 : 0,
85                                           0,
86                                           ttl);
87                               if (error)
88                                         return (error);
89                               stackempty = 0;
90                               m->m_flags |= M_MPLSLABELED;
91                               break;
92                     case MPLSLOP_SWAP:
93                               /*
94                                * Operation is only permmited if label stack
95                                * is not empty.
96                                */
97                               if (stackempty)
98                                         return (ENOTSUP);
99                               KKASSERT(m->m_flags & M_MPLSLABELED);
100                               error = mpls_swap(m, ntohl(smpls->smpls_label));
101                               if (error)
102                                         return (error);
103                               break;
104                     case MPLSLOP_POP:
105                               /*
106                                * Operation is only permmited if label stack
107                                * is not empty.
108                                */
109                               if (stackempty)
110                                         return (ENOTSUP);
111                               KKASSERT(m->m_flags & M_MPLSLABELED);
112                               error = mpls_pop(m, &stackempty);
113                               if (error)
114                                         return (error);
115                               /*
116                                * If we are popping out the last label then
117                                * mark the mbuf as ~M_MPLSLABELED.
118                                */
119                               if (stackempty)
120                                         m->m_flags &= ~M_MPLSLABELED;
121                               break;
122                     default:
123                               /* Unknown label operation */
124                               return (ENOTSUP);
125                     }
126           }
127 
128           return (error);
129 }
130 
131 /*
132  * Returns FALSE if no further output processing required.
133  */
134 boolean_t
mpls_output_process(struct mbuf * m,struct rtentry * rt)135 mpls_output_process(struct mbuf *m, struct rtentry *rt)
136 {
137           int error;
138 
139           /* Does this route have MPLS label operations? */
140           if (!(rt->rt_flags & RTF_MPLSOPS))
141                     return TRUE;
142 
143           error = mpls_output(m, rt);
144           if (error) {
145                     m_freem(m);
146                     return FALSE;
147           }
148 
149           return TRUE;
150 }
151 
152 static int
mpls_push(struct mbuf ** m,mpls_label_t label,mpls_s_t s,mpls_exp_t exp,mpls_ttl_t ttl)153 mpls_push(struct mbuf **m, mpls_label_t label, mpls_s_t s, mpls_exp_t exp, mpls_ttl_t ttl) {
154           struct mpls *mpls;
155           u_int32_t buf = 0;  /* Silence warning */
156 
157           M_PREPEND(*m, sizeof(struct mpls), M_NOWAIT);
158           if (*m == NULL)
159                     return (ENOBUFS);
160 
161           MPLS_SET_LABEL(buf, label);
162           MPLS_SET_STACK(buf, s);
163           MPLS_SET_EXP(buf, exp);
164           MPLS_SET_TTL(buf, ttl);
165           mpls = mtod(*m, struct mpls *);
166           mpls->mpls_shim = htonl(buf);
167 
168           return (0);
169 }
170 
171 static int
mpls_swap(struct mbuf * m,mpls_label_t label)172 mpls_swap(struct mbuf *m, mpls_label_t label) {
173           struct mpls *mpls;
174           u_int32_t buf;
175           mpls_ttl_t ttl;
176 
177           if (m->m_len < sizeof(struct mpls) &&
178              (m = m_pullup(m, sizeof(struct mpls))) == NULL)
179                     return (ENOBUFS);
180 
181           mpls = mtod(m, struct mpls *);
182           buf = ntohl(mpls->mpls_shim);
183           ttl = MPLS_TTL(buf);
184           if (--ttl <= 0) {
185                     /* XXX: should send icmp ttl expired. */
186                     mplsstat.mplss_ttlexpired++;
187                     return (ETIMEDOUT);
188           }
189           MPLS_SET_LABEL(buf, label);
190           MPLS_SET_TTL(buf, ttl); /* XXX tunnel mode: uniform, pipe, short pipe */
191           mpls->mpls_shim = htonl(buf);
192 
193           return (0);
194 }
195 
196 static int
mpls_pop(struct mbuf * m,mpls_s_t * sbit)197 mpls_pop(struct mbuf *m, mpls_s_t *sbit) {
198           struct mpls *mpls;
199           u_int32_t buf;
200 
201           if (m->m_len < sizeof(struct mpls)) {
202                     m = m_pullup(m, sizeof(struct mpls));
203                     if (m == NULL)
204                               return (ENOBUFS);
205           }
206           mpls = mtod(m, struct mpls *);
207           buf = ntohl(mpls->mpls_shim);
208           *sbit = MPLS_STACK(buf);
209 
210           m_adj(m, sizeof(struct mpls));
211 
212           return (0);
213 }
214