struct mpls_ilm* mpls_get_ilm_by_label (struct mpls_label *label, int labelspace, char bos) { struct mpls_ilm *ilm = NULL; /* handle the reserved label range */ if (label->ml_type == MPLS_LABEL_GEN && label->u.ml_gen < 16) { int want_bos = mpls_reserved[label->u.ml_gen].bos; MPLS_DEBUG("%s\n",mpls_reserved[label->u.ml_gen].msg); ilm = mpls_reserved[label->u.ml_gen].ilm; if (unlikely(!ilm)) { MPLS_DEBUG("invalid incoming label, dropping\n"); return NULL; } mpls_ilm_hold(ilm); if ((want_bos && !bos) || (!want_bos && bos)) { mpls_ilm_release (ilm); MPLS_DEBUG("invalid incoming labelstack, dropping\n"); return NULL; } } else { /* not reserved label */ ilm = mpls_get_ilm (mpls_label2key(labelspace,label)); if (unlikely(!ilm)) { MPLS_DEBUG("unknown incoming label, dropping\n"); return NULL; } } return ilm; }
int mpls_del_in_label(struct mpls_in_label_req *in) { struct mpls_ilm *ilm = NULL; struct mpls_label *ml = NULL; unsigned int key = 0; MPLS_ENTER; BUG_ON(!in); ml = &in->mil_label; key = mpls_label2key(/* labelspace*/ ml->ml_index, ml); ilm = mpls_get_ilm(key); if (unlikely(!ilm)) { MPLS_DEBUG("Node %u was not in tree\n",key); MPLS_EXIT; return -ESRCH; } spin_lock_bh (&mpls_ilm_lock); if (atomic_read(&ilm->u.dst.__refcnt) != 2) { /* someone else is hold a refcnt, we can't delete */ /* release the refcnt we aquired in mpls_get_ilm() */ mpls_ilm_release (ilm); spin_unlock_bh (&mpls_ilm_lock); MPLS_DEBUG("Node %u is being used\n",key); MPLS_EXIT; return -EBUSY; } /* * Remove a ILM from the tree */ ilm = mpls_remove_ilm(key); spin_unlock_bh (&mpls_ilm_lock); if (unlikely(!ilm)) { MPLS_DEBUG("Node %u was not in tree\n",key); MPLS_EXIT; return -ESRCH; } __mpls_del_in_label(ilm); MPLS_EXIT; return 0; }
int mpls_set_out_label_mtu(struct mpls_out_label_req *out) { struct mpls_nhlfe *nhlfe = NULL; int retval = 0; unsigned int key; BUG_ON(!out); MPLS_ENTER; key = out->mol_label.u.ml_key; nhlfe = mpls_get_nhlfe(key); if (unlikely(!nhlfe)) { MPLS_DEBUG("Node %u does not exists in radix tree\n", key); MPLS_EXIT; return -ESRCH; } /* Update the MTU if possible */ if (nhlfe->nhlfe_mtu_limit >= out->mol_mtu) { nhlfe->nhlfe_mtu = out->mol_mtu; } else { MPLS_DEBUG("MTU is larger than lower layer (%d > %d)\n", out->mol_mtu, nhlfe->nhlfe_mtu_limit); /* release the refcnt held by mpls_get_nhlfe */ mpls_nhlfe_release(nhlfe); return -EINVAL; } /* release the refcnt held by mpls_get_nhlfe */ mpls_nhlfe_release(nhlfe); /* force the layer 3 protocols to re-find and dsts (NHLFEs), * thus picking up the new MTU */ mpls_proto_cache_flush_all(&init_net); MPLS_EXIT; return retval; }
int mpls_set_nexthop2(struct mpls_nhlfe *nhlfe, struct dst_entry *dst) { MPLS_ENTER; dst->metrics[RTAX_MTU-1] = nhlfe->nhlfe_mtu; dst->child = dst_clone(&nhlfe->u.dst); MPLS_DEBUG("nhlfe: %p mtu: %d dst: %p\n", nhlfe, nhlfe->nhlfe_mtu, &nhlfe->u.dst); MPLS_EXIT; return 0; }
struct mpls_ilm* mpls_ilm_dst_alloc(unsigned int key, struct mpls_label *ml, unsigned short family, struct mpls_instr_elem *instr, int instr_len) { struct mpls_ilm *ilm; int result; MPLS_ENTER; ilm = dst_alloc (&ilm_dst_ops); if (unlikely(!ilm)) goto ilm_dst_alloc_0; memcpy(&(ilm->ilm_label),ml,sizeof(struct mpls_label)); INIT_LIST_HEAD(&ilm->dev_entry); INIT_LIST_HEAD(&ilm->nhlfe_entry); INIT_LIST_HEAD(&ilm->global); ilm->ilm_instr = NULL; ilm->ilm_key = key; ilm->ilm_labelspace = ml->ml_index; ilm->ilm_age = jiffies; ilm->ilm_proto = mpls_proto_find_by_family(family); ilm->ilm_fix_hh = 0; if (unlikely(!ilm->ilm_proto)) { MPLS_DEBUG("Unable to find protocol driver for '0x%04x'\n", family); goto ilm_dst_alloc_1; } else { ilm->u.dst.input = ilm->ilm_proto->local_deliver; } ilm->u.dst.dev = init_net.loopback_dev; result = mpls_set_in_instrs(instr, instr_len, ilm); if (result) goto ilm_dst_alloc_2; MPLS_EXIT; return ilm; /* Error Path */ ilm_dst_alloc_2: mpls_proto_release(ilm->ilm_proto); ilm_dst_alloc_1: ilm->u.dst.obsolete = 1; dst_free(&ilm->u.dst); ilm_dst_alloc_0: MPLS_EXIT; return NULL; }
inline MPLS_IN_OPCODE_PROTOTYPE(mpls_in_op_set_ds) { unsigned short *ds = data; MPLS_ENTER; if (!MPLSCB(*pskb)->bos) { MPLS_DEBUG("SET_DS and not BOS\n"); MPLS_EXIT; return MPLS_RESULT_DROP; } MPLSCB(*pskb)->prot->change_dsfield(*pskb, *ds); MPLS_EXIT; return MPLS_RESULT_SUCCESS; }
int mpls_insert_ilm (unsigned int key, struct mpls_ilm *ilm) { int retval = 0; mpls_ilm_hold (ilm); retval = radix_tree_insert (&mpls_ilm_tree, key, ilm); if (unlikely(retval)) { MPLS_DEBUG("Error create node with key %u in radix tree\n",key); retval = -ENOMEM; } list_add_rcu(&ilm->global, &mpls_ilm_list); return retval; }
static int __mpls_set_labelspace(struct net_device *dev, int labelspace) { struct mpls_interface *mif = dev->mpls_ptr; MPLS_ENTER; BUG_ON(!mif); mif->labelspace = labelspace; MPLS_DEBUG("Set labelspace for %s to %d\n", dev->name, labelspace); MPLS_EXIT; return 0; }
struct mpls_ilm* mpls_remove_ilm (unsigned int key) { struct mpls_ilm *ilm = NULL; MPLS_ENTER; ilm = radix_tree_delete (&mpls_ilm_tree, key); if (!ilm) { MPLS_DEBUG("node key %u not found.\n",key); return NULL; } list_del_rcu(&ilm->global); mpls_ilm_release (ilm); MPLS_EXIT; return ilm; }
struct mpls_nhlfe* mpls_remove_nhlfe (unsigned int key) { struct mpls_nhlfe *nhlfe = NULL; MPLS_ENTER; nhlfe = radix_tree_delete(&mpls_nhlfe_tree, key); if (!nhlfe) MPLS_DEBUG("NHLFE node with key %u not found.\n",key); list_del_rcu(&nhlfe->global); /* release the refcnt for the tree hold it */ mpls_nhlfe_release (nhlfe); MPLS_EXIT; return nhlfe; }
int mpls_del_out_label(struct mpls_out_label_req *out) { struct mpls_nhlfe *nhlfe = NULL; unsigned int key; MPLS_ENTER; key = mpls_label2key(0,&out->mol_label); nhlfe = mpls_get_nhlfe(key); if (unlikely(!nhlfe)) { MPLS_DEBUG("Node %u was not in tree\n",key); MPLS_EXIT; return -ESRCH; } spin_lock_bh (&mpls_nhlfe_lock); /* at this point a NHLFE that can be deleted will have a refcnt * of 2, one from mpls_get_nhlfe() we just executed and the * other that from when it was added to the tree */ if (atomic_read(&nhlfe->__refcnt) > 2) { /* someone else is hold a refcnt, we can't delete */ /* release the refcnt we aquired in mpls_get_nhlfe() */ mpls_nhlfe_release (nhlfe); spin_unlock_bh (&mpls_nhlfe_lock); MPLS_DEBUG("Node %u is being used\n",key); MPLS_EXIT; return -EBUSY; } /* * This code starts the process of removing a NHLFE from the * system. The first thing we we do it remove it from the tree * so no one else can get a reference to it. Then we notify the * higher layer protocols that they should give up thier references * soon (does not need to happen immediatly, the dst system allows * for this. Finally we schedule the RCU system to call * dst_rcu_free() which waits until all CPUs have finished * thier current work and then calls dst_rcu_free() which * kicks the dst system into action once the dst system knows * everyone is done using this "dst" it calls mpls_dst_destroy(). */ /* remove the NHLFE from the tree (which decs the refcnt we held when * it was added to the tree) */ mpls_remove_nhlfe(nhlfe->nhlfe_key); spin_unlock_bh (&mpls_nhlfe_lock); mpls_nhlfe_event(MPLS_CMD_DELNHLFE, nhlfe, 0, 0); /* destrory the instructions on this nhlfe, so as to no longer * hold refs to interfaces and other NHLFEs. * * Remember NHLFEs may stick around in the dst system even * after we've removed it from the tree. So this will result * in traffic using the NHLFE to be dropped */ mpls_destroy_out_instrs (nhlfe); /* let the dst system know we're done with this NHLFE and * schedule all higher layer protocol to give up their references */ dst_release(&nhlfe->u.dst); nhlfe->u.dst.obsolete = 1; mpls_proto_cache_flush_all(&init_net); /* since high layer protocols may still be using us in there caches * we need to use call_rcu() and dst_rcu_free() to take care * of actually cleaning up NHLFE */ call_rcu(&nhlfe->u.dst.rcu_head, dst_rcu_free); /* release the refcnt we aquired in mpls_get_nhlfe() */ mpls_nhlfe_release (nhlfe); MPLS_EXIT; return 0; }
int mpls_add_out_label (struct mpls_out_label_req *out, int seq, int pid) { struct mpls_nhlfe *nhlfe = NULL; unsigned int key = 0; int retval = 0; MPLS_ENTER; BUG_ON(!out); /* Create a new key */ key = mpls_get_out_key(); /* * Check if the NHLFE is already in the tree. * It should not exist. In fact, it is impossible :) */ nhlfe = mpls_get_nhlfe (key); if (unlikely(nhlfe)) { MPLS_DEBUG("Node %u already exists in radix tree\n",key); /* release the refcnt held by mpls_get_nhlfe */ mpls_nhlfe_release (nhlfe); retval = -EEXIST; goto error; } /* * Allocate a new Output Information/Label, */ nhlfe = nhlfe_dst_alloc (key); if (unlikely(!nhlfe)) { retval = -ENOMEM; goto error; } /* Insert into NHLFE tree */ spin_lock_bh (&mpls_nhlfe_lock); if (unlikely(mpls_insert_nhlfe (key,nhlfe))) { spin_unlock_bh (&mpls_nhlfe_lock); nhlfe->u.dst.obsolete = 1; dst_free (&nhlfe->u.dst); goto error; } /* make sure that the dst system doesn't delete this until we're * done with it */ dst_hold(&nhlfe->u.dst); mpls_nhlfe_hold(nhlfe); spin_unlock_bh (&mpls_nhlfe_lock); /* we need to hold a ref to the nhlfe while calling * mpls_nhlfe_event so it can't disappear */ mpls_nhlfe_event(MPLS_CMD_NEWNHLFE, nhlfe, seq, pid); mpls_nhlfe_release(nhlfe); out->mol_label.ml_type = MPLS_LABEL_KEY; out->mol_label.u.ml_key = key; error: MPLS_EXIT; return retval; }
int mpls_detach_in2out(struct mpls_xconnect_req *req) { struct mpls_instr *mi = NULL; struct mpls_nhlfe *nhlfe = NULL; struct mpls_ilm *ilm = NULL; unsigned int key = 0; int labelspace; int ret = 0; MPLS_ENTER; BUG_ON(!req); /* Hold a ref to the ILM, The 'in' segment */ labelspace = req->mx_in.ml_index; key = mpls_label2key(labelspace,&(req->mx_in)); ilm = mpls_get_ilm(key); if (unlikely(!ilm)) { MPLS_DEBUG("Node %u does not exist in radix tree\n",key); ret = -ESRCH; goto err_no_ilm; } /* Check that there is an instruction set! */ if (unlikely(!ilm->ilm_instr)) { MPLS_DEBUG("No instruction Set!") ret = -ESRCH; goto err_no_ilm_instr; } /* Fetch the last instr, make sure it is FWD*/ for (mi = ilm->ilm_instr; mi->mi_next;mi = mi->mi_next); /* nop*/ if (!mi || mi->mi_opcode != MPLS_OP_FWD) { MPLS_DEBUG("opcode not found!\n"); ret = -ENXIO; goto err_no_fwd; } /* Get the current held nhlfe for the last in instr */ nhlfe = mi->mi_data; key = mpls_label2key(0,&(req->mx_out)); /* Make sure it is the good nhlfe */ if (!nhlfe || key != nhlfe->nhlfe_key) { /* Do not release the NHLFE, it was invalid */ MPLS_DEBUG("Invalid NHLFE %u\n",key); ret = -ENXIO; goto err_no_nhlfe; } /* The new last opcode for this ILM is now peek */ mi->mi_opcode = MPLS_OP_PEEK; /* With no data */ mi->mi_data = NULL; /* Release the NHLFE held by the Opcode (cf. mpls_attach_in2out) */ mpls_xc_event(MPLS_CMD_DELXC, ilm, nhlfe); mpls_nhlfe_release(nhlfe); ret = 0; err_no_nhlfe: err_no_fwd: /* Release the ILM after use */ mpls_ilm_release(ilm); err_no_ilm_instr: err_no_ilm: MPLS_EXIT; return ret; }
int mpls_attach_in2out(struct mpls_xconnect_req *req) { struct mpls_instr *mi = NULL; struct mpls_nhlfe *nhlfe = NULL; struct mpls_ilm *ilm = NULL; unsigned short op = 0; int labelspace, key; MPLS_ENTER; labelspace = req->mx_in.ml_index; /* Hold a ref to the ILM */ key = mpls_label2key(labelspace,&(req->mx_in)); ilm = mpls_get_ilm(key); if (unlikely(!ilm)) { MPLS_DEBUG("Node %u does not exist in radix tree\n",key); MPLS_EXIT; return -ESRCH; } /* Hold a ref to the NHLFE */ key = mpls_label2key(0,&(req->mx_out)); nhlfe = mpls_get_nhlfe(key); if (unlikely(!nhlfe)) { MPLS_DEBUG("Node %u does not exist in radix tree\n",key); mpls_ilm_release(ilm); MPLS_EXIT; return -ESRCH; } if (unlikely(!ilm->ilm_instr)) { MPLS_DEBUG("No instruction Set!") mpls_ilm_release(ilm); mpls_nhlfe_release(nhlfe); MPLS_EXIT; return -ESRCH; } /* * Update the instructions: now, instead of "DLV"/"PEEK", now * we "FWD". The NHLFE is not released (is held by the opcode). */ /* Lookup the last instr */ for (mi = ilm->ilm_instr; mi->mi_next;mi = mi->mi_next); /* nop*/ op = mi->mi_opcode; switch (op) { case MPLS_OP_DLV: mi->mi_opcode = MPLS_OP_FWD; mi->mi_data = (void*)nhlfe; break; case MPLS_OP_FWD: mpls_xc_event(MPLS_CMD_DELXC, ilm, _mpls_as_nhlfe(mi->mi_data)); mpls_nhlfe_release(_mpls_as_nhlfe(mi->mi_data)); mi->mi_data = (void*)nhlfe; break; case MPLS_OP_PEEK: mi->mi_opcode = MPLS_OP_FWD; mi->mi_data = (void*)nhlfe; break; } mpls_xc_event(MPLS_CMD_NEWXC, ilm, nhlfe); mpls_ilm_release(ilm); return 0; }
int mpls_add_in_label (const struct mpls_in_label_req *in) { struct mpls_ilm *ilm = NULL; /* New ILM to insert */ struct mpls_label *ml = NULL; /* Requested Label */ unsigned int key = 0; /* Key to use */ int retval = 0; struct mpls_instr_elem instr[2]; MPLS_ENTER; BUG_ON(!in); ml = (struct mpls_label *)&in->mil_label; if (mpls_is_reserved_label(ml)) { MPLS_DEBUG("Unable to add reserved label to ILM\n"); retval = -EINVAL; goto error; } /* Obtain key */ key = mpls_label2key(/* labelspace*/ ml->ml_index, ml); /* Check if the node already exists */ ilm = mpls_get_ilm(key); if (unlikely(ilm)) { printk (MPLS_ERR "MPLS: node %u already exists\n",key); mpls_ilm_release(ilm); retval = -EEXIST; goto error; } /* * Allocate a new input Information/Label, */ instr[0].mir_direction = MPLS_IN; instr[0].mir_opcode = MPLS_OP_POP; instr[1].mir_direction = MPLS_IN; instr[1].mir_opcode = MPLS_OP_PEEK; ilm = mpls_ilm_dst_alloc (key, ml, in->mil_proto, instr, 2); if (unlikely(!ilm)) { retval = -ENOMEM; goto error; } /* Insert into ILM tree */ spin_lock_bh (&mpls_ilm_lock); if (unlikely(mpls_insert_ilm(key,ilm))) { mpls_ilm_release (ilm); spin_unlock_bh (&mpls_ilm_lock); ilm->u.dst.obsolete = 1; dst_free (&ilm->u.dst); retval = -ENOMEM; goto error; } mpls_ilm_hold(ilm); spin_unlock_bh (&mpls_ilm_lock); /* we have hold a refcnt to the ilm across mpls_ilm_event() * to make sure it can't disappear */ mpls_ilm_event(MPLS_CMD_NEWILM, ilm); mpls_ilm_release(ilm); error: MPLS_EXIT; return retval; }
int mpls_push (struct sk_buff **skb, struct mpls_label *ml) { struct sk_buff *o = NULL; struct sk_buff *n = NULL; unsigned int label = 0; u32 shim; MPLS_ENTER; o = *skb; if (unlikely(!ml)) { MPLS_DEBUG("no outgoing label\n"); return MPLS_RESULT_DROP; } try_again: if(likely((MPLSCB(o)->gap >= MPLS_SHIM_SIZE) || (o->data - o->head >= MPLS_SHIM_SIZE))) { /* * if we have room between data and end of mac_header * just shift the data,transport_header,network_header pointers and use the room * this would happen if we had a pop previous to this */ MPLS_DEBUG("using gap\n"); skb_push(o,MPLS_SHIM_SIZE); o->transport_header -= MPLS_SHIM_SIZE; o->network_header -= MPLS_SHIM_SIZE; MPLSCB(o)->gap -= MPLS_SHIM_SIZE; if (MPLSCB(o)->gap < 0) { MPLSCB(o)->gap = 0; } } else { /* * we have no room in the inn, go ahead and create a new sk_buff * with enough extra room for one shim */ MPLS_DEBUG("creating larger packet\n"); if(!(n = skb_realloc_headroom(o, 32))) { return MPLS_RESULT_DROP; } MPLSCB(n)->gap = 0; MPLS_DEBUG("dump old packet\n"); MPLS_DEBUG_CALL(mpls_skb_dump(o)); kfree_skb(o); MPLS_DEBUG("dump new packet\n"); MPLS_DEBUG_CALL(mpls_skb_dump(n)); o = *skb = n; goto try_again; } switch(ml->ml_type) { case MPLS_LABEL_GEN: label = ml->u.ml_gen; break; default: MPLS_DEBUG("invalid label type(%d)\n",ml->ml_type); goto push_end; } /* * no matter what layer 2 we are on, we need the shim! (mpls-encap RFC) */ shim = htonl(((label & 0xFFFFF) << 12) | ((MPLSCB(o)->exp & 0x7) << 9) | ((MPLSCB(o)->bos & 0x1) << 8) | (MPLSCB(o)->ttl & 0xFF)); memmove(o->data,&shim,MPLS_SHIM_SIZE); MPLSCB(o)->label = label; MPLSCB(o)->bos = 0; MPLSCB(o)->popped_bos = 0; push_end: MPLS_EXIT; return MPLS_RESULT_SUCCESS;; }