* Don't call ifp->if_output() from inside mpls_output(). Make the
[dragonfly.git] / sys / netproto / mpls / mpls_output.c
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  * $DragonFly: src/sys/netproto/mpls/mpls_output.c,v 1.2 2008/08/05 15:11:32 nant Exp $
32  */
33
34 #include <sys/param.h>
35 #include <sys/mbuf.h>
36 #include <sys/systm.h>
37
38 #include <net/if_var.h>
39
40 #include <netinet/ip.h>
41
42 #include <netproto/mpls/mpls.h>
43 #include <netproto/mpls/mpls_var.h>
44
45 static int mpls_push(struct mbuf **, mpls_label_t,
46                      mpls_s_t, mpls_exp_t, mpls_ttl_t);
47 static int mpls_swap(struct mbuf *, mpls_label_t);
48 static int mpls_pop(struct mbuf *, mpls_s_t *);
49
50 int
51 mpls_output(struct mbuf *m, struct rtentry *rt)
52 {
53         struct sockaddr_mpls *smpls = NULL;
54         int error = 0, i;
55         mpls_s_t stackempty;
56         mpls_ttl_t ttl = 255;
57         struct ip *ip;
58
59         M_ASSERTPKTHDR(m);
60
61         /*
62          * Check if we are coming from an MPLS routing table lookup.
63          * The rt_key of this rtentry will have a family AF_MPLS if so.
64          */
65         stackempty = rt_key(rt)->sa_family != AF_MPLS ? 1 : 0;
66         if (stackempty) {
67                 switch (rt_key(rt)->sa_family) {
68                 case AF_INET:
69                         ip = mtod(m, struct ip *);
70                         ttl = ip->ip_ttl;
71                         break;
72                 }
73         }
74
75         for (i=0; i < MPLS_MAXLOPS && rt->rt_shim[i] != NULL; ++i) {
76                 smpls = (struct sockaddr_mpls *)rt->rt_shim[i];
77                 switch (smpls->smpls_op) {
78                 case MPLSLOP_PUSH:
79                         error = mpls_push(&m,
80                                   ntohl(smpls->smpls_label),
81                                   /*
82                                    * If we are the first label push, then
83                                    * set the bottom-of-stack bit.
84                                    */
85                                   (stackempty && i == 0) ? 1 : 0,
86                                   0,
87                                   ttl);
88                         if (error)
89                                 return (error);
90                         stackempty = 0;
91                         m->m_flags |= M_MPLSLABELED;
92                         break;
93                 case MPLSLOP_SWAP:
94                         /*
95                          * Operation is only permmited if label stack
96                          * is not empty.
97                          */
98                         if (stackempty)
99                                 return (ENOTSUP);
100                         KKASSERT(m->m_flags & M_MPLSLABELED);
101                         error = mpls_swap(m, ntohl(smpls->smpls_label));
102                         if (error)
103                                 return (error);
104                         break;
105                 case MPLSLOP_POP:
106                         /*
107                          * Operation is only permmited if label stack
108                          * is not empty.
109                          */
110                         if (stackempty)
111                                 return (ENOTSUP);
112                         KKASSERT(m->m_flags & M_MPLSLABELED);
113                         error = mpls_pop(m, &stackempty);
114                         if (error)
115                                 return (error);
116                         /*
117                          * If we are popping out the last label then
118                          * mark the mbuf as ~M_MPLSLABELED.
119                          */
120                         if (stackempty)
121                                 m->m_flags &= ~M_MPLSLABELED;
122                         break;
123                 default:
124                         /* Unknown label operation */
125                         return (ENOTSUP);
126                 }
127         }
128         
129         return (error);
130 }
131
132 /*
133  * Returns FALSE if no further output processing required.
134  */
135 boolean_t
136 mpls_output_process(struct mbuf *m, struct rtentry *rt)
137 {
138         int error;
139
140         /* Does this route have MPLS label operations? */
141         if (!(rt->rt_flags & RTF_MPLSOPS))
142                 return TRUE;
143
144         error = mpls_output(m, rt);
145         if (error) {
146                 m_freem(m);
147                 return FALSE;
148         }
149
150         return TRUE;
151 }
152
153 static int
154 mpls_push(struct mbuf **m, mpls_label_t label, mpls_s_t s, mpls_exp_t exp, mpls_ttl_t ttl) {
155         struct mpls *mpls;
156         u_int32_t buf = 0;      /* Silence warning */
157
158         M_PREPEND(*m, sizeof(struct mpls), MB_DONTWAIT);
159         if (*m == NULL)
160                 return (ENOBUFS);
161
162         MPLS_SET_LABEL(buf, label);
163         MPLS_SET_STACK(buf, s);
164         MPLS_SET_EXP(buf, exp);
165         MPLS_SET_TTL(buf, ttl);
166         mpls = mtod(*m, struct mpls *);
167         mpls->mpls_shim = htonl(buf);
168         
169         return (0);
170 }
171
172 static int
173 mpls_swap(struct mbuf *m, mpls_label_t label) {
174         struct mpls *mpls;
175         u_int32_t buf;
176         mpls_ttl_t ttl;
177
178         if (m->m_len < sizeof(struct mpls) &&
179            (m = m_pullup(m, sizeof(struct mpls))) == NULL)
180                 return (ENOBUFS);
181
182         mpls = mtod(m, struct mpls *);
183         buf = ntohl(mpls->mpls_shim);
184         ttl = MPLS_TTL(buf);
185         if (--ttl <= 0) {
186                 /* XXX: should send icmp ttl expired. */
187                 mplsstat.mplss_ttlexpired++;
188                 return (ETIMEDOUT);
189         }
190         MPLS_SET_LABEL(buf, label);
191         MPLS_SET_TTL(buf, ttl); /* XXX tunnel mode: uniform, pipe, short pipe */
192         mpls->mpls_shim = htonl(buf);
193         
194         return (0);
195 }
196
197 static int
198 mpls_pop(struct mbuf *m, mpls_s_t *sbit) {
199         struct mpls *mpls;
200         u_int32_t buf;
201
202         if (m->m_len < sizeof(struct mpls)) {
203                 m = m_pullup(m, sizeof(struct mpls));
204                 if (m == NULL)
205                         return (ENOBUFS);
206         }
207         mpls = mtod(m, struct mpls *);
208         buf = ntohl(mpls->mpls_shim);
209         *sbit = MPLS_STACK(buf);
210
211         m_adj(m, sizeof(struct mpls));
212
213         return (0);
214 }