Change NHLFE MTU calculation
[mpls-linux.git] / src / mpls6.c
blobe50c6162906d0ddeb9505f423128800490aacda8
1 /* mpls6.c: IPv6 MPLS protocol driver.
3 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
5 * Changes:
6 * JLEU: Add ICMP handling stubs
7 * Add nexthop printing
8 * Change nexthop resolve signature
9 * JLEU: Added mpls6_cache_flush()
10 * JLEU: un/register reserved labels in fini/init
11 * JLEU: remove sysfs print routin
14 #include <linux/module.h>
15 #include <linux/socket.h>
16 #include <linux/skbuff.h>
17 #include <linux/in6.h>
18 #include <linux/init.h>
19 #include <linux/seq_file.h>
20 #include <net/dsfield.h>
21 #include <net/neighbour.h>
22 #include <net/ipv6.h>
23 #include <net/ip6_route.h>
24 #include <net/ip6_fib.h>
25 #include <net/dst.h>
26 #include <net/mpls.h>
28 MODULE_LICENSE("GPL");
30 extern int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev);
32 static void mpls6_cache_flush(struct net *net)
34 fib6_run_gc((unsigned long)0, net);
37 static void mpls6_set_ttl(struct sk_buff *skb, int ttl)
39 /*ipv6_hdr(skb)->hop_limit; RCAS*/
40 ipv6_hdr(skb)->hop_limit = ttl;
43 static int mpls6_get_ttl(struct sk_buff *skb)
45 return ipv6_hdr(skb)->hop_limit;
48 static void mpls6_change_dsfield(struct sk_buff *skb, int ds)
50 ipv6_change_dsfield(ipv6_hdr(skb), 0x3, ds);
53 static int mpls6_get_dsfield(struct sk_buff *skb)
55 return ipv6_get_dsfield(ipv6_hdr(skb));
58 /* Policy decision, several options:
60 * 1) Silently discard
61 * 2) Pops all MPLS headers, use resulting upper-layer
62 * protocol packet to generate ICMP.
63 * 3) Walk down MPLS headers to upper-layer header,
64 * generate ICMP using that and then prepend
65 * IDENTICAL MPLS header stack to ICMP packet.
67 * Problem with #2 is that there may be no route to
68 * upper-level packet source for us to use. (f.e. we
69 * are switching VPN packets that we have no routes to).
71 * Option #3 should work even in those cases, because it
72 * is more likely that egress of this MPLS path knows how
73 * to route such packets back to source. It should also
74 * not be susceptible to loops in MPLS fabric, since one
75 * never responds to ICMP with ICMP. It is deliberate
76 * assumption made about upper-layer protocol.
78 static int mpls6_ttl_expired(struct sk_buff **skb)
80 return NET_RX_DROP;
83 static int mpls6_mtu_exceeded(struct sk_buff **skb, int mtu)
85 return MPLS_RESULT_DROP;
88 static int mpls6_local_deliver(struct sk_buff *skb)
90 skb->protocol = htons(ETH_P_IPV6);
91 memset(skb->cb, 0, sizeof(skb->cb));
92 dst_release(skb_dst(skb));
93 skb_dst_set(skb, NULL);
94 return ipv6_rcv(skb, skb->dev, NULL, skb->dev);
97 static int mpls6_nexthop_resolve(struct neighbour **np, struct sockaddr *sock_addr, struct net_device *dev)
99 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) sock_addr;
100 struct flowi fl = { .oif = dev->ifindex,
101 .nl_u = { .ip6_u = {.daddr = addr->sin6_addr } } };
102 struct dst_entry *dst;
103 int err;
105 if (addr->sin6_family != AF_INET6)
106 return -EINVAL;
108 dst = ip6_route_output(&init_net, NULL, &fl);
110 err = 0;
111 if (dst->error)
112 err = -EINVAL;
114 if (!err)
115 *np = neigh_clone(dst->neighbour);
118 dst_release(dst);
120 return err;
123 static struct mpls_prot_driver mpls6_driver = {
124 .name = "ipv6",
125 .family = AF_INET6,
126 .ethertype = __constant_htons(ETH_P_IPV6),
127 .cache_flush = mpls6_cache_flush,
128 .set_ttl = mpls6_set_ttl,
129 .get_ttl = mpls6_get_ttl,
130 .change_dsfield = mpls6_change_dsfield,
131 .get_dsfield = mpls6_get_dsfield,
132 .ttl_expired = mpls6_ttl_expired,
133 .mtu_exceeded = mpls6_mtu_exceeded,
134 .local_deliver = mpls6_local_deliver,
135 .nexthop_resolve = mpls6_nexthop_resolve,
136 .owner = THIS_MODULE,
139 static int __init mpls6_init(void)
141 struct mpls_instr_elem instr[2];
142 struct mpls_label ml;
143 struct mpls_ilm *ilm;
144 int result = mpls_proto_add(&mpls6_driver);
146 printk("MPLS: IPv6 over MPLS support\n");
148 if (result)
149 return result;
151 ml.ml_type = MPLS_LABEL_GEN;
152 ml.u.ml_gen = MPLS_IPV6_EXPLICIT_NULL;
154 instr[0].mir_direction = MPLS_IN;
155 instr[0].mir_opcode = MPLS_OP_POP;
156 instr[1].mir_direction = MPLS_IN;
157 instr[1].mir_opcode = MPLS_OP_DLV;
159 ilm = mpls_ilm_dst_alloc(0, &ml, AF_INET6, instr, 2);
160 if (!ilm)
161 return -ENOMEM;
163 result = mpls_add_reserved_label(MPLS_IPV6_EXPLICIT_NULL, ilm);
164 if (result) {
165 ilm->u.dst.obsolete = 1;
166 dst_free(&ilm->u.dst);
167 return result;
170 return 0;
173 static void __exit mpls6_fini(void)
175 struct mpls_ilm *ilm = mpls_del_reserved_label(MPLS_IPV6_EXPLICIT_NULL);
176 mpls_proto_remove(&mpls6_driver);
178 if (ilm) {
179 mpls_ilm_release(ilm);
180 ilm->u.dst.obsolete = 1;
181 call_rcu(&ilm->u.dst.rcu_head, dst_rcu_free);
185 module_init(mpls6_init);
186 module_exit(mpls6_fini);