-#ifndef HAVE_GRE_HANDLE_OFFLOADS
-#ifndef HAVE_GRE_CISCO_REGISTER
-
-#ifdef HAVE_DEMUX_PARSE_GRE_HEADER
-static __sum16 check_checksum(struct sk_buff *skb)
-{
- __sum16 csum = 0;
-
- switch (skb->ip_summed) {
- case CHECKSUM_COMPLETE:
- csum = csum_fold(skb->csum);
-
- if (!csum)
- break;
- /* Fall through. */
-
- case CHECKSUM_NONE:
- skb->csum = 0;
- csum = __skb_checksum_complete(skb);
- skb->ip_summed = CHECKSUM_COMPLETE;
- break;
- }
-
- return csum;
-}
-
-static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
- bool *csum_err)
-{
- unsigned int ip_hlen = ip_hdrlen(skb);
- struct gre_base_hdr *greh;
- __be32 *options;
- int hdr_len;
-
- if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
- return -EINVAL;
-
- greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
- if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
- return -EINVAL;
-
- tpi->flags = gre_flags_to_tnl_flags(greh->flags);
- hdr_len = ip_gre_calc_hlen(tpi->flags);
- tpi->hdr_len = hdr_len;
- tpi->proto = greh->protocol;
-
- if (!pskb_may_pull(skb, hdr_len))
- return -EINVAL;
-
- options = (__be32 *)(greh + 1);
- if (greh->flags & GRE_CSUM) {
- if (check_checksum(skb)) {
- *csum_err = true;
- return -EINVAL;
- }
- options++;
- }
-
- if (greh->flags & GRE_KEY) {
- tpi->key = *options;
- options++;
- } else
- tpi->key = 0;
-
- if (unlikely(greh->flags & GRE_SEQ)) {
- tpi->seq = *options;
- options++;
- } else
- tpi->seq = 0;
-
- /* WCCP version 1 and 2 protocol decoding.
- * - Change protocol to IP
- * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
- */
- if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
- tpi->proto = htons(ETH_P_IP);
- if ((*(u8 *)options & 0xF0) != 0x40) {
- hdr_len += 4;
- if (!pskb_may_pull(skb, hdr_len))
- return -EINVAL;
- }
- }
-
- return iptunnel_pull_header(skb, hdr_len, tpi->proto, false);
-}
-
-static struct gre_cisco_protocol __rcu *gre_cisco_proto;
-static int gre_cisco_rcv(struct sk_buff *skb)
-{
- struct gre_cisco_protocol *proto;
- struct tnl_ptk_info tpi;
- bool csum_err = false;
-
- rcu_read_lock();
- proto = rcu_dereference(gre_cisco_proto);
- if (!proto)
- goto drop;
-
- if (parse_gre_header(skb, &tpi, &csum_err) < 0)
- goto drop;
- proto->handler(skb, &tpi);
- rcu_read_unlock();
- return 0;
-
-drop:
- rcu_read_unlock();
- kfree_skb(skb);
- return 0;
-}
-
-static const struct gre_protocol ipgre_protocol = {
- .handler = gre_cisco_rcv,
-};
-
-int rpl_gre_cisco_register(struct gre_cisco_protocol *newp)
-{
- int err;
-
- err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);
- if (err) {
- pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__);
- return err;
- }
-
-
- return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ?
- 0 : -EBUSY;
-}
-EXPORT_SYMBOL_GPL(rpl_gre_cisco_register);
-
-int rpl_gre_cisco_unregister(struct gre_cisco_protocol *proto)
-{
- int ret;
- ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ?
- 0 : -EINVAL;
-
- if (ret)
- return ret;
-
- synchronize_net();
- ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
- return ret;
-}
-EXPORT_SYMBOL_GPL(rpl_gre_cisco_unregister);
-
-#endif /* HAVE_DEMUX_PARSE_GRE_HEADER */
-#endif /* !HAVE_GRE_CISCO_REGISTER */
-#endif
-