1 Star 0 Fork 0

ronaldoa/pimd

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
pim_proto.c 113.33 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600
/*
* Copyright (c) 1998-2001
* University of Southern California/Information Sciences Institute.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* $Id: pim_proto.c,v 1.47 2003/05/28 22:57:16 pavlin Exp $
*/
#include <arpa/inet.h>
#include "defs.h"
typedef struct {
uint16_t holdtime;
uint32_t dr_prio;
int8_t dr_prio_present;
uint32_t genid;
} pim_hello_opts_t;
/*
* Local functions definitions.
*/
static int restart_dr_election (struct uvif *v);
static int parse_pim_hello (char *msg, size_t len, uint32_t src, pim_hello_opts_t *opts);
static void cache_nbr_settings (pim_nbr_entry_t *nbr, pim_hello_opts_t *opts);
static int send_pim_register_stop (uint32_t reg_src, uint32_t reg_dst, uint32_t inner_grp, uint32_t inner_source);
static build_jp_message_t *get_jp_working_buff (void);
static void return_jp_working_buff (pim_nbr_entry_t *pim_nbr);
static void pack_jp_message_grp (pim_nbr_entry_t *pim_nbr);
static void pack_jp_message_rp (pim_nbr_entry_t *pim_nbr);
static void send_jp_message (pim_nbr_entry_t *pim_nbr);
static int compare_metrics (uint32_t local_preference,
uint32_t local_metric,
uint32_t local_address,
uint32_t remote_preference,
uint32_t remote_metric,
uint32_t remote_address);
build_jp_message_t *build_jp_message_pool;
int build_jp_message_pool_counter;
/************************************************************************
* PIM_HELLO
************************************************************************/
int receive_pim_hello(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
{
vifi_t vifi;
struct uvif *v;
size_t bsr_length;
pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr;
pim_hello_opts_t opts;
srcentry_t *srcentry;
mrtentry_t *mrtentry;
if (inet_cksum((uint16_t *)msg, len))
return FALSE;
vifi = find_vif_direct(src);
if (vifi == NO_VIF) {
/* Either a local vif or somehow received PIM_HELLO from
* non-directly connected router. Ignore it. */
if (local_address(src) == NO_VIF)
logit(LOG_DEBUG, 0, "Ignoring PIM_HELLO from non-neighbor router %s",
inet_fmt(src, s1, sizeof(s1)));
return FALSE;
}
v = &uvifs[vifi];
if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_REGISTER))
return FALSE; /* Shoudn't come on this interface */
/* Get the Holdtime (in seconds) and any DR priority from the message. Return if error. */
if (parse_pim_hello(msg, len, src, &opts) == FALSE)
return FALSE;
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u",
inet_fmt(src, s1, sizeof(s1)), opts.holdtime);
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_DEBUG, 0, "PIM DR PRIORITY from %s is %u",
inet_fmt(src, s1, sizeof(s1)), opts.dr_prio);
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_DEBUG, 0, "PIM GenID from %s is %u",
inet_fmt(src, s1, sizeof(s1)), opts.genid);
for (prev_nbr = NULL, nbr = v->uv_pim_neighbors; nbr; prev_nbr = nbr, nbr = nbr->next) {
/* The PIM neighbors are sorted in decreasing order of the
* network addresses (note that to be able to compare them
* correctly we must translate the addresses in host order.
*/
if (ntohl(src) < ntohl(nbr->address))
continue;
if (src == nbr->address) {
/* We already have an entry for this host */
if (0 == opts.holdtime) {
/* Looks like we have a nice neighbor who is going down
* and wants to inform us by sending "holdtime=0". Thanks
* buddy and see you again!
*/
logit(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down",
inet_fmt(src, s1, sizeof(s1)));
delete_pim_nbr(nbr);
return TRUE;
}
/* https://tools.ietf.org/html/draft-ietf-pim-hello-genid-01 */
if (nbr->genid != opts.genid) {
/* Known neighbor rebooted, update info and resend RP-Set */
cache_nbr_settings(nbr, &opts);
goto rebooted;
}
if (nbr->dr_prio != opts.dr_prio) {
/* New DR priority for neighbor, restart DR election */
cache_nbr_settings(nbr, &opts);
goto election;
}
cache_nbr_settings(nbr, &opts);
return TRUE;
}
/* No entry for this neighbor. Exit loop to create an entry for it. */
break;
}
/*
* This is a new neighbor. Create a new entry for it.
* It must be added right after `prev_nbr`
*/
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_INFO, 0, "Received PIM HELLO from new neighbor %s", inet_fmt(src, s1, sizeof(s1)));
new_nbr = calloc(1, sizeof(pim_nbr_entry_t));
if (!new_nbr)
logit(LOG_ERR, 0, "Ran out of memory in receive_pim_hello()");
new_nbr->address = src;
new_nbr->vifi = vifi;
new_nbr->build_jp_message = NULL;
new_nbr->next = nbr;
new_nbr->prev = prev_nbr;
/* Add PIM Hello options */
cache_nbr_settings(new_nbr, &opts);
/* Add to linked list of neighbors */
if (prev_nbr)
prev_nbr->next = new_nbr;
else
v->uv_pim_neighbors = new_nbr;
if (new_nbr->next)
new_nbr->next->prev = new_nbr;
v->uv_flags &= ~VIFF_NONBRS;
v->uv_flags |= VIFF_PIM_NBR;
/* Since a new neighbour has come up, let it know your existence */
/* XXX: TODO: not in the spec,
* but probably should send the message after a short random period?
*/
send_pim_hello(v, pim_timer_hello_holdtime);
rebooted:
if (v->uv_flags & VIFF_DR) {
/*
* If I am the current DR on that interface, so
* send an RP-Set message to the new neighbor.
*/
if ((bsr_length = create_pim_bootstrap_message(pim_send_buf)))
send_pim_unicast(pim_send_buf, v->uv_mtu, v->uv_lcl_addr, src, PIM_BOOTSTRAP, bsr_length);
}
election:
if (restart_dr_election(v)) {
/* I was the DR, but not anymore. Remove all register_vif from
* oif list for all directly connected sources (for vifi). */
/* TODO: XXX: first entry is not used! */
for (srcentry = srclist->next; srcentry; srcentry = srcentry->next) {
/* If not directly connected source for vifi */
if ((srcentry->incoming != vifi) || srcentry->upstream)
continue;
for (mrtentry = srcentry->mrtlink; mrtentry; mrtentry = mrtentry->srcnext) {
if (!(mrtentry->flags & MRTF_SG))
continue; /* This is not (S,G) entry */
/* Remove the register oif */
VIFM_CLR(reg_vif_num, mrtentry->joined_oifs);
change_interfaces(mrtentry,
mrtentry->incoming,
mrtentry->joined_oifs,
mrtentry->pruned_oifs,
mrtentry->leaves,
mrtentry->asserted_oifs, 0);
}
}
}
/*
* TODO: XXX: does a new neighbor change any routing entries info?
* Need to trigger joins?
*/
IF_DEBUG(DEBUG_PIM_HELLO)
dump_vifs(stderr); /* Show we got a new neighbor */
return TRUE;
}
void delete_pim_nbr(pim_nbr_entry_t *nbr_delete)
{
srcentry_t *src;
srcentry_t *src_next;
mrtentry_t *mrt;
mrtentry_t *mrt_srcs;
grpentry_t *grp;
cand_rp_t *cand_rp;
rp_grp_entry_t *rp_grp;
rpentry_t *rp;
struct uvif *v;
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_INFO, 0, "Deleting PIM neighbor %s", inet_fmt(nbr_delete->address, s1, sizeof(s1)));
v = &uvifs[nbr_delete->vifi];
/* Delete the entry from the pim_nbrs chain */
if (nbr_delete->prev)
nbr_delete->prev->next = nbr_delete->next;
else
v->uv_pim_neighbors = nbr_delete->next;
if (nbr_delete->next)
nbr_delete->next->prev = nbr_delete->prev;
return_jp_working_buff(nbr_delete);
/* That neighbor could've been the DR */
restart_dr_election(v);
/* Update the source entries */
for (src = srclist; src; src = src_next) {
src_next = src->next;
if (src->upstream != nbr_delete)
continue;
/* Reset the next hop (PIM) router */
if (set_incoming(src, PIM_IIF_SOURCE) == FALSE) {
/* Coudn't reset it. Sorry, the hext hop router toward that
* source is probably not a PIM router, or cannot find route
* at all, hence I cannot handle this source and have to
* delete it.
*/
logit(LOG_WARNING, 0, "Delete source entry for source %s", inet_fmt(src->address, s1, sizeof(s1)));
delete_srcentry(src);
} else if (src->upstream) {
/* Ignore the local or directly connected sources */
/* Browse all MRT entries for this source and reset the
* upstream router. Note that the upstream router is not always
* toward the source: it could be toward the RP for example.
*/
for (mrt = src->mrtlink; mrt; mrt = mrt->srcnext) {
if (!(mrt->flags & MRTF_RP)) {
mrt->upstream = src->upstream;
mrt->metric = src->metric;
mrt->preference = src->preference;
change_interfaces(mrt, src->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
}
}
}
/* Update the RP entries */
for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) {
if (cand_rp->rpentry->upstream != nbr_delete)
continue;
rp = cand_rp->rpentry;
/* Reset the RP entry iif
* TODO: check if error setting the iif! */
if (local_address(rp->address) == NO_VIF) {
set_incoming(rp, PIM_IIF_RP);
} else {
rp->incoming = reg_vif_num;
rp->upstream = NULL;
}
mrt = rp->mrtlink;
if (mrt) {
mrt->upstream = rp->upstream;
mrt->metric = rp->metric;
mrt->preference = rp->preference;
change_interfaces(mrt,
rp->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
/* Update the group entries for this RP */
for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
for (grp = rp_grp->grplink; grp; grp = grp->rpnext) {
mrt = grp->grp_route;
if (mrt) {
mrt->upstream = rp->upstream;
mrt->metric = rp->metric;
mrt->preference = rp->preference;
change_interfaces(mrt,
rp->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
/* Update only the (S,G)RPbit entries for this group */
for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) {
if (mrt_srcs->flags & MRTF_RP) {
mrt_srcs->upstream = rp->upstream;
mrt_srcs->metric = rp->metric;
mrt_srcs->preference = rp->preference;
change_interfaces(mrt_srcs,
rp->incoming,
mrt_srcs->joined_oifs,
mrt_srcs->pruned_oifs,
mrt_srcs->leaves,
mrt_srcs->asserted_oifs, 0);
}
}
}
}
}
/* Fix GitHub issue #22: Crash in (S,G) state when neighbor is lost */
for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) {
for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
for (grp = rp_grp->grplink; grp; grp = grp->next) {
mrt = grp->grp_route;
if (mrt && mrt->upstream) {
if (mrt->upstream == nbr_delete)
mrt->upstream = NULL;
}
}
}
}
free(nbr_delete);
}
/*
* If all PIM routers on a network segment support DR-Priority we use
* that to elect the DR, and use the highest IP address as the tie
* breaker. If any routers does *not* support DR-Priority all routers
* must use the IP address to elect the DR, this for backwards compat.
*
* Returns TRUE if we lost the DR role, elected another router.
*/
static int restart_dr_election(struct uvif *v)
{
int was_dr = 0, use_dr_prio = 1;
uint32_t best_dr_prio = 0;
pim_nbr_entry_t *nbr;
if (v->uv_flags & VIFF_DR)
was_dr = 1;
if (!v->uv_pim_neighbors) {
/* This was our last neighbor, now we're it. */
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_DEBUG, 0, "All neighbor PIM routers on %s lost, we are the DR now.",
inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
v->uv_flags &= ~VIFF_PIM_NBR;
v->uv_flags |= (VIFF_NONBRS | VIFF_DR);
return FALSE;
}
/* Check if all routers on segment advertise DR Priority option
* in their PIM Hello messages. Figure out highest prio. */
for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next) {
if (!nbr->dr_prio_present) {
use_dr_prio = 0;
break;
}
if (nbr->dr_prio > best_dr_prio)
best_dr_prio = nbr->dr_prio;
}
/*
* RFC4601 sec. 4.3.2
*/
if (use_dr_prio) {
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_DEBUG, 0, "All routers in %s segment support DR Priority based DR election.",
inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
if (best_dr_prio < v->uv_dr_prio) {
v->uv_flags |= VIFF_DR;
return FALSE;
}
if (best_dr_prio == v->uv_dr_prio)
goto tiebreak;
} else {
tiebreak:
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_DEBUG, 0, "Using fallback DR election on %s.",
inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
if (ntohl(v->uv_lcl_addr) > ntohl(v->uv_pim_neighbors->address)) {
/* The first address is the new potential remote
* DR address, but the local address is the winner. */
v->uv_flags |= VIFF_DR;
return FALSE;
}
}
if (was_dr) {
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_INFO, 0, "We lost DR role on %s in election.",
inet_fmt(v->uv_lcl_addr, s1, sizeof(s1)));
v->uv_flags &= ~VIFF_DR;
return TRUE; /* Lost election, clean up. */
}
return FALSE;
}
static int validate_pim_opt(uint32_t src, char *str, uint16_t len, uint16_t opt_len)
{
if (len != opt_len) {
IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
logit(LOG_DEBUG, 0, "PIM HELLO %s from %s: invalid OptionLength = %u",
str, inet_fmt(src, s1, sizeof(s1)), opt_len);
return FALSE;
}
return TRUE;
}
static int parse_pim_hello(char *msg, size_t len, uint32_t src, pim_hello_opts_t *opts)
{
int result = FALSE;
size_t rec_len;
uint8_t *data;
uint16_t opt_type;
uint16_t opt_len;
/* Assume no opts. */
memset(opts, 0, sizeof(*opts));
/* Body of PIM message */
msg += sizeof(pim_header_t);
/* Ignore any data if shorter than (pim_hello header) */
for (len -= sizeof(pim_header_t); len >= sizeof(pim_hello_t); len -= rec_len) {
data = (uint8_t *)msg;
GET_HOSTSHORT(opt_type, data);
GET_HOSTSHORT(opt_len, data);
switch (opt_type) {
case PIM_HELLO_HOLDTIME:
result = validate_pim_opt(src, "Holdtime", PIM_HELLO_HOLDTIME_LEN, opt_len);
if (TRUE == result)
GET_HOSTSHORT(opts->holdtime, data);
break;
case PIM_HELLO_DR_PRIO:
result = validate_pim_opt(src, "DR Priority", PIM_HELLO_DR_PRIO_LEN, opt_len);
if (TRUE == result) {
opts->dr_prio_present = 1;
GET_HOSTLONG(opts->dr_prio, data);
}
break;
case PIM_HELLO_GENID:
result = validate_pim_opt(src, "GenID", PIM_HELLO_GENID_LEN, opt_len);
if (TRUE == result)
GET_HOSTLONG(opts->genid, data);
break;
default:
break; /* Ignore any unknown options */
}
/* Move to the next option */
rec_len = (sizeof(pim_hello_t) + opt_len);
if (len < rec_len || result == FALSE)
return FALSE;
msg += rec_len;
}
return result;
}
static void cache_nbr_settings(pim_nbr_entry_t *nbr, pim_hello_opts_t *opts)
{
SET_TIMER(nbr->timer, opts->holdtime);
nbr->genid = opts->genid;
nbr->dr_prio = opts->dr_prio;
nbr->dr_prio_present = opts->dr_prio_present;
}
int send_pim_hello(struct uvif *v, uint16_t holdtime)
{
char *buf;
uint8_t *data;
size_t len;
buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
data = (uint8_t *)buf;
PUT_HOSTSHORT(PIM_HELLO_HOLDTIME, data);
PUT_HOSTSHORT(PIM_HELLO_HOLDTIME_LEN, data);
PUT_HOSTSHORT(holdtime, data);
PUT_HOSTSHORT(PIM_HELLO_DR_PRIO, data);
PUT_HOSTSHORT(PIM_HELLO_DR_PRIO_LEN, data);
PUT_HOSTLONG(v->uv_dr_prio, data);
#ifdef ENABLE_PIM_HELLO_GENID
PUT_HOSTSHORT(PIM_HELLO_GENID, data);
PUT_HOSTSHORT(PIM_HELLO_GENID_LEN, data);
PUT_HOSTLONG(v->uv_genid, data);
#endif
len = data - (uint8_t *)buf;
send_pim(pim_send_buf, v->uv_lcl_addr, allpimrouters_group, PIM_HELLO, len);
SET_TIMER(v->uv_hello_timer, pim_timer_hello_interval);
return TRUE;
}
/************************************************************************
* PIM_REGISTER
************************************************************************/
/* TODO: XXX: IF THE BORDER BIT IS SET, THEN
* FORWARD THE WHOLE PACKET FROM USER SPACE
* AND AT THE SAME TIME IGNORE ANY CACHE_MISS
* SIGNALS FROM THE KERNEL.
*/
int receive_pim_register(uint32_t reg_src, uint32_t reg_dst, char *msg, size_t len)
{
uint32_t inner_src, inner_grp;
pim_register_t *reg;
struct ip *ip;
uint32_t is_border, is_null;
mrtentry_t *mrtentry;
mrtentry_t *mrtentry2;
vifbitmap_t oifs;
/*
* If instance specific multicast routing table is in use, check
* that we are the target of the register packet. Otherwise we
* might end up responding to register packet belonging to another
* pimd instance. If we are not an RP candidate, we shouldn't have
* pimreg interface and shouldn't receive register packets, but we'll
* check the cand_rp flag anyway, just to be on the safe side.
*/
if (mrt_table_id != 0) {
if (!cand_rp_flag || my_cand_rp_address != reg_dst) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "PIM register: packet from %s to %s is not destined for us",
inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)));
return FALSE;
}
}
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_INFO, 0, "Received PIM register: len = %d from %s",
len, inet_fmt(reg_src, s1, sizeof(s1)));
/*
* Message length validation.
* This is suppose to be done in the kernel, but some older kernel
* versions do not pefrorm the check for the NULL register messages.
*/
if (len < sizeof(pim_header_t) + sizeof(pim_register_t) + sizeof(struct ip)) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_INFO, 0, "PIM register: short packet (len = %d) from %s",
len, inet_fmt(reg_src, s1, sizeof(s1)));
return FALSE;
}
/*
* XXX: For PIM_REGISTER the checksum does not include
* the inner IP packet. However, some older routers might
* create the checksum over the whole packet. Hence,
* verify the checksum over the first 8 bytes, and if fails,
* then over the whole Register
*/
if ((inet_cksum((uint16_t *)msg, sizeof(pim_header_t) + sizeof(pim_register_t)))
&& (inet_cksum((uint16_t *)msg, len))) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "PIM REGISTER from DR %s: invalid PIM header checksum",
inet_fmt(reg_src, s1, sizeof(s1)));
return FALSE;
}
/* Lookup register message flags */
reg = (pim_register_t *)(msg + sizeof(pim_header_t));
is_border = ntohl(reg->reg_flags) & PIM_REGISTER_BORDER_BIT;
is_null = ntohl(reg->reg_flags) & PIM_REGISTER_NULL_REGISTER_BIT;
/* initialize the pointer to the encapsulated packet */
ip = (struct ip *)(msg + sizeof(pim_header_t) + sizeof(pim_register_t));
/* check the IP version (especially for the NULL register...see above) */
if (ip->ip_v != IPVERSION && (! is_null)) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_INFO, 0, "PIM register: incorrect IP version (%d) of the inner packet from %s",
ip->ip_v, inet_fmt(reg_src, s1, sizeof(s1)));
return FALSE;
}
/* We are keeping all addresses in network order, so no need for ntohl()*/
inner_src = ip->ip_src.s_addr;
inner_grp = ip->ip_dst.s_addr;
/*
* inner_src and inner_grp must be valid IP unicast and multicast address
* respectively. XXX: not in the spec.
* PIM-SSM support: inner_grp must not be in PIM-SSM range
*/
if ((!inet_valid_host(inner_src)) || (!IN_MULTICAST(ntohl(inner_grp))) || IN_PIM_SSM_RANGE(inner_grp)) {
if (!inet_valid_host(inner_src)) {
logit(LOG_WARNING, 0, "Inner source address of register message by %s is invalid: %s",
inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
}
if (!IN_MULTICAST(ntohl(inner_grp))) {
logit(LOG_WARNING, 0, "Inner group address of register message by %s is invalid: %s",
inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2)));
}
send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
return FALSE;
}
mrtentry = find_route(inner_src, inner_grp, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
if (!mrtentry) {
/* No routing entry. Send REGISTER_STOP and return. */
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "No routing entry for source %s and/or group %s" ,
inet_fmt(inner_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2)));
/* TODO: XXX: shouldn't it be inner_src=INADDR_ANY? Not in the spec. */
send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
/* Create mrtentry for forthcoming join requests. Once one occurs
* we know who is sending to this group and can send join to
* that immediately. Saves 0 - 60 seconds not to wait next
* PIM Register.
*/
mrtentry = find_route(inner_src, inner_grp, MRTF_SG, CREATE);
if (!mrtentry)
return TRUE;
SET_TIMER(mrtentry->timer, PIM_DATA_TIMEOUT);
mrtentry->flags &= ~MRTF_NEW;
change_interfaces(mrtentry,
mrtentry->incoming,
mrtentry->joined_oifs,
mrtentry->pruned_oifs,
mrtentry->leaves,
mrtentry->asserted_oifs, 0);
return TRUE;
}
/* Check if I am the RP for that group */
if ((local_address(reg_dst) == NO_VIF) || !check_mrtentry_rp(mrtentry, reg_dst)) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "Not RP in address %s", inet_fmt(reg_dst, s1, sizeof(s1)));
send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
return TRUE;
}
/* I am the RP */
if (mrtentry->flags & MRTF_SG) {
/* (S,G) found */
/* TODO: check the timer again */
SET_TIMER(mrtentry->timer, PIM_DATA_TIMEOUT); /* restart timer */
if (!(mrtentry->flags & MRTF_SPT)) { /* The SPT bit is not set */
if (!is_null) {
calc_oifs(mrtentry, &oifs);
if (VIFM_ISEMPTY(oifs) && (mrtentry->incoming == reg_vif_num)) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "No output intefaces found for group %s source %s",
inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
return TRUE;
}
/*
* TODO: XXX: BUG!!!
* The data will be forwarded by the kernel MFC!!!
* Need to set a special flag for this routing entry so after
* a cache miss occur, the multicast packet will be forwarded
* from user space and won't install entry in the kernel MFC.
* The problem is that the kernel MFC doesn't know the
* PMBR address and simply sets the multicast forwarding
* cache to accept/forward all data coming from the
* register_vif.
*/
if (is_border) {
if (mrtentry->pmbr_addr != reg_src) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "pmbr_addr (%s) != reg_src (%s)",
inet_fmt(mrtentry->pmbr_addr, s1, sizeof(s1)), inet_fmt(reg_src, s2, sizeof(s2)));
send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
return TRUE;
}
}
return TRUE;
}
/* TODO: XXX: if NULL_REGISTER and has (S,G) with SPT=0, then..?*/
return TRUE;
}
else {
/* The SPT bit is set */
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "SPT bit is set for group %s source %s",
inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
return TRUE;
}
}
if (mrtentry->flags & (MRTF_WC | MRTF_PMBR)) {
if (is_border) {
/* Create (S,G) state. The oifs will be the copied from the
* existing (*,G) or (*,*,RP) entry. */
mrtentry2 = find_route(inner_src, inner_grp, MRTF_SG, CREATE);
if (mrtentry2) {
mrtentry2->pmbr_addr = reg_src;
/* Clear the SPT flag */
mrtentry2->flags &= ~(MRTF_SPT | MRTF_NEW);
SET_TIMER(mrtentry2->timer, PIM_DATA_TIMEOUT);
/* TODO: explicitly call the Join/Prune send function? */
FIRE_TIMER(mrtentry2->jp_timer); /* Send the Join immediately */
/* TODO: explicitly call this function?
send_pim_join_prune(mrtentry2->upstream->vifi,
mrtentry2->upstream,
PIM_JOIN_PRUNE_HOLDTIME);
*/
}
}
}
if (mrtentry->flags & MRTF_WC) {
/* First PIM Register for this routing entry, log it */
logit(LOG_INFO, 0, "Received PIM REGISTER: src %s, group %s",
inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2)));
/* (*,G) entry */
calc_oifs(mrtentry, &oifs);
if (VIFM_ISEMPTY(oifs)) {
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "No output intefaces found for group %s source %s (*,G)",
inet_fmt(inner_grp, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2)));
send_pim_register_stop(reg_dst, reg_src, inner_grp, INADDR_ANY_N);
return FALSE;
} else { /* XXX: TODO: check with the spec again */
if (!is_null) {
uint32_t mfc_source = inner_src;
/* Install cache entry in the kernel */
/* TODO: XXX: probably redundant here, because the
* decapsulated mcast packet in the kernel will
* result in CACHE_MISS
*/
#ifdef KERNEL_MFC_WC_G
if (!(mrtentry->flags & MRTF_MFC_CLONE_SG))
mfc_source = INADDR_ANY_N;
#endif /* KERNEL_MFC_WC_G */
add_kernel_cache(mrtentry, mfc_source, inner_grp, 0);
k_chg_mfc(igmp_socket, mfc_source, inner_grp,
mrtentry->incoming, mrtentry->oifs,
mrtentry->group->rpaddr);
return TRUE;
}
}
return TRUE;
}
if (mrtentry->flags & MRTF_PMBR) {
/* (*,*,RP) entry */
if (!is_null) {
uint32_t mfc_source = inner_src;
/* XXX: have to create either (S,G) or (*,G).
* The choice below is (*,G)
*/
mrtentry2 = find_route(INADDR_ANY_N, inner_grp, MRTF_WC, CREATE);
if (!mrtentry2)
return FALSE;
if (mrtentry2->flags & MRTF_NEW) {
/* TODO: something else? Have the feeling sth is missing */
mrtentry2->flags &= ~MRTF_NEW;
/* TODO: XXX: copy the timer from the (*,*,RP) entry? */
COPY_TIMER(mrtentry->timer, mrtentry2->timer);
}
/* Install cache entry in the kernel */
#ifdef KERNEL_MFC_WC_G
if (!(mrtentry->flags & MRTF_MFC_CLONE_SG))
mfc_source = INADDR_ANY_N;
#endif /* KERNEL_MFC_WC_G */
add_kernel_cache(mrtentry, mfc_source, inner_grp, 0);
k_chg_mfc(igmp_socket, mfc_source, inner_grp,
mrtentry->incoming, mrtentry->oifs,
mrtentry2->group->rpaddr);
return TRUE;
}
}
/* Shoudn't happen: invalid routing entry? */
/* XXX: TODO: shoudn't be inner_src=INADDR_ANY? Not in the spec. */
IF_DEBUG(DEBUG_PIM_REGISTER)
logit(LOG_DEBUG, 0, "Shoudn't happen: invalid routing entry? (%s, %s, %s, %s)",
inet_fmt(reg_dst, s1, sizeof(s1)), inet_fmt(reg_src, s2, sizeof(s2)),
inet_fmt(inner_grp, s3, sizeof(s3)), inet_fmt(inner_src, s4, sizeof(s4)));
send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src);
return TRUE;
}
int send_pim_register(char *packet)
{
struct ip *ip;
uint32_t source, group;
vifi_t vifi;
rpentry_t *rpentry;
mrtentry_t *mrtentry;
mrtentry_t *mrtentry2;
uint32_t reg_src, reg_dst;
int reg_mtu, pktlen = 0;
char *buf;
ip = (struct ip *)packet;
source = ip->ip_src.s_addr;
group = ip->ip_dst.s_addr;
if (IN_PIM_SSM_RANGE(group))
return FALSE; /* Group is in PIM-SSM range, don't send register. */
if ((vifi = find_vif_direct_local(source)) == NO_VIF)
return FALSE;
if (!(uvifs[vifi].uv_flags & VIFF_DR))
return FALSE; /* I am not the DR for that subnet */
rpentry = rp_match(group);
if (!rpentry)
return FALSE; /* No RP for this group */
if (local_address(rpentry->address) != NO_VIF) {
/* TODO: XXX: not sure it is working! */
return FALSE; /* I am the RP for this group */
}
mrtentry = find_route(source, group, MRTF_SG, CREATE);
if (!mrtentry)
return FALSE; /* Cannot create (S,G) state */
if (mrtentry->flags & MRTF_NEW) {
/* A new entry, log it */
reg_src = uvifs[vifi].uv_lcl_addr;
reg_dst = mrtentry->group->rpaddr;
logit(LOG_INFO, 0, "Send PIM REGISTER: src %s dst %s, group %s",
inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)),
inet_fmt(group, s3, sizeof(s3)));
mrtentry->flags &= ~MRTF_NEW;
RESET_TIMER(mrtentry->rs_timer); /* Reset the Register-Suppression timer */
mrtentry2 = mrtentry->group->grp_route;
if (!mrtentry2)
mrtentry2 = mrtentry->group->active_rp_grp->rp->rpentry->mrtlink;
if (mrtentry2) {
FIRE_TIMER(mrtentry2->jp_timer); /* Timeout the Join/Prune timer */
/* TODO: explicitly call this function?
send_pim_join_prune(mrtentry2->upstream->vifi,
mrtentry2->upstream,
PIM_JOIN_PRUNE_HOLDTIME);
*/
}
}
/* Restart the (S,G) Entry-timer */
SET_TIMER(mrtentry->timer, PIM_DATA_TIMEOUT);
IF_TIMER_NOT_SET(mrtentry->rs_timer) {
/* The Register-Suppression Timer is not running.
* Encapsulate the data and send to the RP.
*/
buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
memset(buf, 0, sizeof(pim_register_t)); /* No flags set */
buf += sizeof(pim_register_t);
/* Copy the data packet at the back of the register packet */
pktlen = ntohs(ip->ip_len);
memcpy(buf, ip, pktlen);
pktlen += sizeof(pim_register_t); /* 'sizeof(struct ip) + sizeof(pim_header_t)' added by send_pim() */
reg_mtu = uvifs[vifi].uv_mtu; /* XXX: Use PMTU to RP instead! */
reg_src = uvifs[vifi].uv_lcl_addr;
reg_dst = mrtentry->group->rpaddr;
send_pim_unicast(pim_send_buf, reg_mtu, reg_src, reg_dst, PIM_REGISTER, pktlen);
return TRUE;
}
return TRUE;
}
int send_pim_null_register(mrtentry_t *mrtentry)
{
struct ip *ip;
pim_register_t *pim_register;
int reg_mtu, pktlen;
vifi_t vifi;
uint32_t reg_src, reg_dst;
/* No directly connected source; no local address */
if ((vifi = find_vif_direct_local(mrtentry->source->address))== NO_VIF)
return FALSE;
pim_register = (pim_register_t *)(pim_send_buf + sizeof(struct ip) +
sizeof(pim_header_t));
memset(pim_register, 0, sizeof(pim_register_t));
pim_register->reg_flags = htonl(pim_register->reg_flags
| PIM_REGISTER_NULL_REGISTER_BIT);
ip = (struct ip *)(pim_register + 1);
/* set src/dst in dummy hdr */
ip->ip_v = IPVERSION;
ip->ip_hl = (sizeof(struct ip) >> 2);
ip->ip_tos = 0;
ip->ip_id = 0;
ip->ip_off = 0;
ip->ip_p = IPPROTO_UDP; /* XXX: bogus */
ip->ip_len = htons(sizeof(struct ip));
ip->ip_ttl = MINTTL; /* TODO: XXX: check whether need to setup the ttl */
ip->ip_src.s_addr = mrtentry->source->address;
ip->ip_dst.s_addr = mrtentry->group->group;
ip->ip_sum = 0;
ip->ip_sum = inet_cksum((uint16_t *)ip, sizeof(struct ip));
/* include the dummy ip header */
pktlen = sizeof(pim_register_t) + sizeof(struct ip);
reg_mtu = uvifs[vifi].uv_mtu;
reg_dst = mrtentry->group->rpaddr;
reg_src = uvifs[vifi].uv_lcl_addr;
send_pim_unicast(pim_send_buf, reg_mtu, reg_src, reg_dst, PIM_REGISTER, pktlen);
return TRUE;
}
/************************************************************************
* PIM_REGISTER_STOP
************************************************************************/
int receive_pim_register_stop(uint32_t reg_src, uint32_t reg_dst, char *msg, size_t len)
{
pim_encod_grp_addr_t egaddr;
pim_encod_uni_addr_t eusaddr;
uint8_t *data;
mrtentry_t *mrtentry;
vifbitmap_t pruned_oifs;
/* Checksum */
if (inet_cksum((uint16_t *)msg, len))
return FALSE;
data = (uint8_t *)(msg + sizeof(pim_header_t));
GET_EGADDR(&egaddr, data);
GET_EUADDR(&eusaddr, data);
logit(LOG_INFO, 0, "Received PIM_REGISTER_STOP from RP %s to %s for src = %s and group = %s",
inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)),
inet_fmt(eusaddr.unicast_addr, s3, sizeof(s3)),
inet_fmt(egaddr.mcast_addr, s4, sizeof(s4)));
/* TODO: apply the group mask and do register_stop for all grp addresses */
/* TODO: check for SourceAddress == 0 */
mrtentry = find_route(eusaddr.unicast_addr, egaddr.mcast_addr, MRTF_SG, DONT_CREATE);
if (!mrtentry)
return FALSE;
/* XXX: not in the spec: check if the PIM_REGISTER_STOP originator is
* really the RP
*/
if (check_mrtentry_rp(mrtentry, reg_src) == FALSE)
return FALSE;
/* restart the Register-Suppression timer */
SET_TIMER(mrtentry->rs_timer, (0.5 * PIM_REGISTER_SUPPRESSION_TIMEOUT)
+ (RANDOM() % (PIM_REGISTER_SUPPRESSION_TIMEOUT + 1)));
/* Prune the register_vif from the outgoing list */
VIFM_COPY(mrtentry->pruned_oifs, pruned_oifs);
VIFM_SET(reg_vif_num, pruned_oifs);
change_interfaces(mrtentry, mrtentry->incoming,
mrtentry->joined_oifs, pruned_oifs,
mrtentry->leaves,
mrtentry->asserted_oifs, 0);
return TRUE;
}
/* TODO: optional rate limiting is not implemented yet */
/* Unicasts a REGISTER_STOP message to the DR */
static int
send_pim_register_stop(uint32_t reg_src, uint32_t reg_dst, uint32_t inner_grp, uint32_t inner_src)
{
char *buf;
uint8_t *data;
if (IN_PIM_SSM_RANGE(inner_grp))
return TRUE;
logit(LOG_INFO, 0, "Send PIM REGISTER STOP from %s to router %s for src = %s and group = %s",
inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)),
inet_fmt(inner_src, s3, sizeof(s3)), inet_fmt(inner_grp, s4, sizeof(s4)));
buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
data = (uint8_t *)buf;
PUT_EGADDR(inner_grp, SINGLE_GRP_MSKLEN, 0, data);
PUT_EUADDR(inner_src, data);
send_pim_unicast(pim_send_buf, 0, reg_src, reg_dst, PIM_REGISTER_STOP, data - (uint8_t *)buf);
return TRUE;
}
/************************************************************************
* PIM_JOIN_PRUNE
************************************************************************/
int join_or_prune(mrtentry_t *mrtentry, pim_nbr_entry_t *upstream_router)
{
vifbitmap_t entry_oifs;
mrtentry_t *mrtentry_grp;
if (!mrtentry || !upstream_router)
return PIM_ACTION_NOTHING;
calc_oifs(mrtentry, &entry_oifs);
if (mrtentry->flags & (MRTF_PMBR | MRTF_WC)) {
if (IN_PIM_SSM_RANGE(mrtentry->group->group)) {
logit(LOG_DEBUG, 0, "No action for SSM (PMBR|WC)");
return PIM_ACTION_NOTHING;
}
/* (*,*,RP) or (*,G) entry */
/* The (*,*,RP) or (*,G) J/P messages are sent only toward the RP */
if (upstream_router != mrtentry->upstream)
return PIM_ACTION_NOTHING;
/* TODO: XXX: Can we have (*,*,RP) prune? */
if (VIFM_ISEMPTY(entry_oifs)) {
/* NULL oifs */
if (!(uvifs[mrtentry->incoming].uv_flags & VIFF_DR))
/* I am not the DR for that subnet. */
return PIM_ACTION_PRUNE;
if (VIFM_ISSET(mrtentry->incoming, mrtentry->leaves))
/* I am the DR and have local leaves */
return PIM_ACTION_JOIN;
/* Probably the last local member hast timeout */
return PIM_ACTION_PRUNE;
}
return PIM_ACTION_JOIN;
}
if (mrtentry->flags & MRTF_SG) {
/* (S,G) entry */
/* TODO: check again */
if (mrtentry->upstream == upstream_router) {
if (!(mrtentry->flags & MRTF_RP)) {
/* Upstream router toward S */
if (VIFM_ISEMPTY(entry_oifs)) {
if (mrtentry->group->active_rp_grp &&
mrtentry->group->rpaddr == my_cand_rp_address) {
/* (S,G) at the RP. Don't send Join/Prune
* (see the end of Section 3.3.2)
*/
return PIM_ACTION_NOTHING;
}
return PIM_ACTION_PRUNE;
}
else {
return PIM_ACTION_JOIN;
}
}
else {
if (IN_PIM_SSM_RANGE(mrtentry->group->group)) {
logit(LOG_DEBUG, 0, "No action for SSM (RP)");
return PIM_ACTION_NOTHING;
}
/* Upstream router toward RP */
if (VIFM_ISEMPTY(entry_oifs))
return PIM_ACTION_PRUNE;
}
}
/* Looks like the case when the upstream router toward S is
* different from the upstream router toward RP
*/
if (!mrtentry->group->active_rp_grp)
return PIM_ACTION_NOTHING;
mrtentry_grp = mrtentry->group->grp_route;
if (!mrtentry_grp) {
mrtentry_grp = mrtentry->group->active_rp_grp->rp->rpentry->mrtlink;
if (!mrtentry_grp)
return PIM_ACTION_NOTHING;
}
if (mrtentry_grp->upstream != upstream_router)
return PIM_ACTION_NOTHING; /* XXX: shoudn't happen */
if (!(mrtentry->flags & MRTF_RP) && (mrtentry->flags & MRTF_SPT))
return PIM_ACTION_PRUNE;
}
return PIM_ACTION_NOTHING;
}
/*
* Log PIM Join/Prune message. Send log event for every join and prune separately.
* Format of the Join/Prune message starting from dataptr is following:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Multicast Group Address 1 (Encoded-Group format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Number of Joined Sources | Number of Pruned Sources |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Joined Source Address 1 (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| . |
| . |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Joined Source Address n (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Pruned Source Address 1 (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| . |
| . |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Pruned Source Address n (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Multicast Group Address m (Encoded-Group format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Number of Joined Sources | Number of Pruned Sources |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Joined Source Address 1 (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| . |
| . |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Joined Source Address n (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Pruned Source Address 1 (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| . |
| . |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Pruned Source Address n (Encoded-Source format) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
void log_pim_join_prune(uint32_t src, uint8_t *data_ptr, int num_groups, char* ifname)
{
pim_encod_grp_addr_t encod_group;
pim_encod_src_addr_t encod_src;
uint32_t group, source;
uint16_t num_j_srcs;
uint16_t num_p_srcs;
/* Message validity check is done by caller */
while (num_groups--) {
GET_EGADDR(&encod_group, data_ptr);
GET_HOSTSHORT(num_j_srcs, data_ptr);
GET_HOSTSHORT(num_p_srcs, data_ptr);
group = encod_group.mcast_addr;
while(num_j_srcs--) {
GET_ESADDR(&encod_src, data_ptr);
source = encod_src.src_addr;
logit(LOG_INFO, 0, "Received PIM JOIN from %s to group %s for multicast source %s on %s",
inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3)), ifname);
}
while (num_p_srcs--) {
GET_ESADDR(&encod_src, data_ptr);
source = encod_src.src_addr;
logit(LOG_INFO, 0, "Received PIM PRUNE from %s to group %s for multicast source %s on %s",
inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(source, s3, sizeof(s3)), ifname);
}
}
}
/* TODO: when parsing, check if we go beyond message size */
/* TODO: too long, simplify it! */
#define PIM_JOIN_PRUNE_MINLEN (4 + PIM_ENCODE_UNI_ADDR_LEN + 4)
int receive_pim_join_prune(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
{
vifi_t vifi;
struct uvif *v;
pim_encod_uni_addr_t eutaddr;
pim_encod_grp_addr_t egaddr;
pim_encod_src_addr_t esaddr;
uint8_t *data;
uint8_t *data_start;
uint8_t *data_group_end;
uint8_t num_groups;
uint8_t num_groups_tmp;
int star_star_rp_found;
uint16_t holdtime;
uint16_t num_j_srcs;
uint16_t num_j_srcs_tmp;
uint16_t num_p_srcs;
uint32_t source;
uint32_t group;
uint32_t s_mask;
uint32_t g_mask;
uint8_t s_flags;
uint8_t reserved __attribute__((unused));
rpentry_t *rpentry;
mrtentry_t *mrt;
mrtentry_t *mrt_srcs;
mrtentry_t *mrt_rp;
grpentry_t *grp;
uint16_t jp_value;
pim_nbr_entry_t *upstream_router;
int my_action;
int ignore_group;
rp_grp_entry_t *rp_grp;
uint8_t *data_group_j_start;
uint8_t *data_group_p_start;
uint32_t new_join;
if ((vifi = find_vif_direct(src)) == NO_VIF) {
/* Either a local vif or somehow received PIM_JOIN_PRUNE from
* non-directly connected router. Ignore it.
*/
if (local_address(src) == NO_VIF) {
logit(LOG_DEBUG, 0, "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s",
inet_fmt(src, s1, sizeof(s1)));
}
return FALSE;
}
/* Checksum */
if (inet_cksum((uint16_t *)msg, len))
return FALSE;
v = &uvifs[vifi];
if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER))
return FALSE; /* Shoudn't come on this interface */
/* sanity check for the minimum length */
if (len < PIM_JOIN_PRUNE_MINLEN) {
logit(LOG_NOTICE, 0, "%s(): Too short Join/Prune message (%u bytes) from %s on %s",
__func__, len, inet_fmt(src, s1, sizeof(s1)), v->uv_name);
return FALSE;
}
len -= PIM_JOIN_PRUNE_MINLEN;
data = (uint8_t *)(msg + sizeof(pim_header_t));
/* Get the target address */
GET_EUADDR(&eutaddr, data);
GET_BYTE(reserved, data);
GET_BYTE(num_groups, data);
GET_HOSTSHORT(holdtime, data);
if (num_groups == 0) {
/* No indication for groups in the message */
logit(LOG_NOTICE, 0, "%s(): No groups in Join/Prune message from %s on %s!",
__func__, inet_fmt(src, s1, sizeof(s1)), v->uv_name);
return FALSE;
}
logit(LOG_INFO, 0, "Received PIM JOIN/PRUNE from %s on %s",
inet_fmt(src, s1, sizeof(s1)), v->uv_name);
/* Sanity check for the message length through all the groups */
num_groups_tmp = num_groups;
data_start = data;
while (num_groups_tmp--) {
size_t srclen;
/* group addr + #join + #src */
if (len < PIM_ENCODE_GRP_ADDR_LEN + sizeof(uint32_t)) {
logit(LOG_NOTICE, 0, "%s(): Join/Prune message from %s on %s is"
" too short to contain enough data",
__func__, inet_fmt(src, s1, sizeof(s1)), v->uv_name);
return FALSE;
}
len -= (PIM_ENCODE_GRP_ADDR_LEN + sizeof(uint32_t));
data += PIM_ENCODE_GRP_ADDR_LEN;
/* joined source addresses and pruned source addresses */
GET_HOSTSHORT(num_j_srcs, data);
GET_HOSTSHORT(num_p_srcs, data);
srclen = (num_j_srcs + num_p_srcs) * PIM_ENCODE_SRC_ADDR_LEN;
if (len < srclen) {
logit(LOG_NOTICE, 0, "%s(): Join/Prune message from %s on %s is"
" too short to contain enough data", __func__,
inet_fmt(src, s1, sizeof(s1)), v->uv_name);
return FALSE;
}
len -= srclen;
data += srclen;
}
data = data_start;
num_groups_tmp = num_groups;
/* Sanity check is done. Log the message */
log_pim_join_prune(src, data, num_groups, v->uv_name);
if (eutaddr.unicast_addr != v->uv_lcl_addr) {
/* if I am not the target of the join message */
/* Join/Prune suppression code. This either modifies the J/P timers
* or triggers an overriding Join.
*/
/* Note that if we have (S,G) prune and (*,G) Join, we must send
* them in the same message. We don't bother to modify both timers
* here. The Join/Prune sending function will take care of that.
*/
upstream_router = find_pim_nbr(eutaddr.unicast_addr);
if (!upstream_router)
return FALSE; /* I have no such neighbor */
while (num_groups--) {
GET_EGADDR(&egaddr, data);
GET_HOSTSHORT(num_j_srcs, data);
GET_HOSTSHORT(num_p_srcs, data);
MASKLEN_TO_MASK(egaddr.masklen, g_mask);
group = egaddr.mcast_addr;
if (!IN_MULTICAST(ntohl(group))) {
data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
continue; /* Ignore this group and jump to the next */
}
if ((ntohl(group) == CLASSD_PREFIX) && (egaddr.masklen == STAR_STAR_RP_MSKLEN)) {
/* (*,*,RP) Join suppression */
while (num_j_srcs--) {
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
MASKLEN_TO_MASK(esaddr.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
/* This is the RP address. */
rpentry = rp_find(source);
if (!rpentry)
continue; /* Don't have such RP. Ignore */
mrt_rp = rpentry->mrtlink;
my_action = join_or_prune(mrt_rp, upstream_router);
if (my_action != PIM_ACTION_JOIN)
continue;
/* Check the holdtime */
/* TODO: XXX: TIMER implem. dependency! */
if (mrt_rp->jp_timer > holdtime)
continue;
if ((mrt_rp->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))
continue;
/* Set the Join/Prune suppression timer for this
* routing entry by increasing the current
* Join/Prune timer.
*/
jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
/* TODO: XXX: TIMER implem. dependency! */
if (mrt_rp->jp_timer < jp_value)
SET_TIMER(mrt_rp->jp_timer, jp_value);
}
} /* num_j_srcs */
while (num_p_srcs--) {
/* TODO: XXX: Can we have (*,*,RP) prune message?
* Not in the spec, but anyway, the code below
* can handle them: either suppress
* the local (*,*,RP) prunes or override the prunes by
* sending (*,*,RP) and/or (*,G) and/or (S,G) Join.
*/
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
MASKLEN_TO_MASK(esaddr.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
/* This is the RP address. */
rpentry = rp_find(source);
if (!rpentry)
continue; /* Don't have such RP. Ignore */
mrt_rp = rpentry->mrtlink;
my_action = join_or_prune(mrt_rp, upstream_router);
if (my_action == PIM_ACTION_PRUNE) {
/* TODO: XXX: TIMER implem. dependency! */
if ((mrt_rp->jp_timer < holdtime)
|| ((mrt_rp->jp_timer == holdtime) &&
(ntohl(src) > ntohl(v->uv_lcl_addr)))) {
/* Suppress the Prune */
jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
if (mrt_rp->jp_timer < jp_value)
SET_TIMER(mrt_rp->jp_timer, jp_value);
}
} else if (my_action == PIM_ACTION_JOIN) {
/* Override the Prune by scheduling a Join */
jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
/* TODO: XXX: TIMER implem. dependency! */
if (mrt_rp->jp_timer > jp_value)
SET_TIMER(mrt_rp->jp_timer, jp_value);
}
/* Check all (*,G) and (S,G) matching to this RP.
* If my_action == JOIN, then send a Join and override
* the (*,*,RP) Prune.
*/
for (grp = rpentry->cand_rp->rp_grp_next->grplink; grp; grp = grp->rpnext) {
my_action = join_or_prune(grp->grp_route, upstream_router);
if (my_action == PIM_ACTION_JOIN) {
jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
/* TODO: XXX: TIMER implem. dependency! */
if (grp->grp_route->jp_timer > jp_value)
SET_TIMER(grp->grp_route->jp_timer, jp_value);
}
for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) {
my_action = join_or_prune(mrt_srcs, upstream_router);
if (my_action == PIM_ACTION_JOIN) {
jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
/* TODO: XXX: TIMER implem. dependency! */
if (mrt_srcs->jp_timer > jp_value)
SET_TIMER(mrt_srcs->jp_timer, jp_value);
}
} /* For all (S,G) */
} /* For all (*,G) */
}
} /* num_p_srcs */
continue; /* This was (*,*,RP) suppression */
}
/* (*,G) or (S,G) suppression */
/* TODO: XXX: currently, accumulated groups
* (i.e. group_masklen < egaddress_lengt) are not
* implemented. Just need to create a loop and apply the
* procedure below for all groups matching the prefix.
*/
while (num_j_srcs--) {
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
MASKLEN_TO_MASK(esaddr.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
/* (*,G) JOIN_REQUEST (toward the RP) */
mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
if (!mrt)
continue;
my_action = join_or_prune(mrt, upstream_router);
if (my_action != PIM_ACTION_JOIN)
continue;
/* (*,G) Join suppresion */
if (source != mrt->group->active_rp_grp->rp->rpentry->address)
continue; /* The RP address doesn't match. Ignore. */
/* Check the holdtime */
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->jp_timer > holdtime)
continue;
if ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))
continue;
jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
if (mrt->jp_timer < jp_value)
SET_TIMER(mrt->jp_timer, jp_value);
continue;
} /* End of (*,G) Join suppression */
/* (S,G) Join suppresion */
mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
if (!mrt)
continue;
my_action = join_or_prune(mrt, upstream_router);
if (my_action != PIM_ACTION_JOIN)
continue;
/* Check the holdtime */
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->jp_timer > holdtime)
continue;
if ((mrt->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))
continue;
jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
if (mrt->jp_timer < jp_value)
SET_TIMER(mrt->jp_timer, jp_value);
continue;
}
/* Prunes suppression */
while (num_p_srcs--) {
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
MASKLEN_TO_MASK(esaddr.masklen, s_mask);
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
/* (*,G) prune suppression */
rpentry = rp_match(group);
if (!rpentry || (rpentry->address != source))
continue; /* No such RP or it is different. Ignore */
mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
if (!mrt)
continue;
my_action = join_or_prune(mrt, upstream_router);
if (my_action == PIM_ACTION_PRUNE) {
/* TODO: XXX: TIMER implem. dependency! */
if ((mrt->jp_timer < holdtime)
|| ((mrt->jp_timer == holdtime)
&& (ntohl(src) > ntohl(v->uv_lcl_addr)))) {
/* Suppress the Prune */
jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
if (mrt->jp_timer < jp_value)
SET_TIMER(mrt->jp_timer, jp_value);
}
}
else if (my_action == PIM_ACTION_JOIN) {
/* Override the Prune by scheduling a Join */
jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->jp_timer > jp_value)
SET_TIMER(mrt->jp_timer, jp_value);
}
/* Check all (S,G) entries for this group.
* If my_action == JOIN, then send the Join and override
* the (*,G) Prune.
*/
for (mrt_srcs = mrt->group->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) {
my_action = join_or_prune(mrt_srcs, upstream_router);
if (my_action == PIM_ACTION_JOIN) {
jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->jp_timer > jp_value)
SET_TIMER(mrt->jp_timer, jp_value);
}
} /* For all (S,G) */
continue; /* End of (*,G) prune suppression */
}
/* (S,G) prune suppression */
mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
if (!mrt)
continue;
my_action = join_or_prune(mrt, upstream_router);
if (my_action == PIM_ACTION_PRUNE) {
/* Suppress the (S,G) Prune */
/* TODO: XXX: TIMER implem. dependency! */
if ((mrt->jp_timer < holdtime)
|| ((mrt->jp_timer == holdtime)
&& (ntohl(src) > ntohl(v->uv_lcl_addr)))) {
jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD);
if (mrt->jp_timer < jp_value)
SET_TIMER(mrt->jp_timer, jp_value);
}
}
else if (my_action == PIM_ACTION_JOIN) {
/* Override the Prune by scheduling a Join */
jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10;
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->jp_timer > jp_value)
SET_TIMER(mrt->jp_timer, jp_value);
}
} /* while (num_p_srcs--) */
} /* while (num_groups--) */
return TRUE;
} /* End of Join/Prune suppression code */
/* I am the target of this join, so process the message */
/* The spec says that if there is (*,G) Join, it has priority over
* old existing ~(S,G) prunes in the routing table.
* However, if the (*,G) Join and the ~(S,G) prune are in
* the same message, ~(S,G) has the priority. The spec doesn't say it,
* but I think the same is true for (*,*,RP) and ~(S,G) prunes.
*
* The code below do:
* (1) Check the whole message for (*,*,RP) Joins.
* (1.1) If found, clean all pruned_oifs for all (*,G) and all (S,G)
* for each RP in the list, but do not update the kernel cache.
* Then go back to the beginning of the message and start
* processing for each group:
* (2) Check for Prunes. If no prunes, process the Joins.
* (3) If there are Prunes:
* (3.1) Scan the Join part for existing (*,G) Join.
* (3.1.1) If there is (*,G) Join, clear join interface from
* the pruned_oifs for all (S,G), but DO NOT flush the
* change to the kernel (by using change_interfaces()
* for example)
* (3.2) After the pruned_oifs are eventually cleared in (3.1.1),
* process the Prune part of the message normally
* (setting the prune_oifs and flashing the changes to the (kernel).
* (3.3) After the Prune part is processed, process the Join part
* normally (by applying any changes to the kernel)
* (4) If there were (*,*,RP) Join/Prune, process them.
*
* If the Join/Prune list is too long, it may result in long processing
* overhead. The idea above is not to place any wrong info in the
* kernel, because it may result in short-time existing traffic
* forwarding on wrong interface.
* Hopefully, in the future will find a better way to implement it.
*/
num_groups_tmp = num_groups;
data_start = data;
star_star_rp_found = FALSE; /* Indicating whether we have (*,*,RP) join */
while (num_groups_tmp--) {
/* Search for (*,*,RP) Join */
GET_EGADDR(&egaddr, data);
GET_HOSTSHORT(num_j_srcs, data);
GET_HOSTSHORT(num_p_srcs, data);
group = egaddr.mcast_addr;
if ((ntohl(group) != CLASSD_PREFIX) || (egaddr.masklen != STAR_STAR_RP_MSKLEN)) {
/* This is not (*,*,RP). Jump to the next group. */
data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
continue;
}
/* (*,*,RP) found. For each RP and each (*,G) and each (S,G) clear
* the pruned oif, but do not update the kernel.
*/
star_star_rp_found = TRUE;
while (num_j_srcs--) {
GET_ESADDR(&esaddr, data);
rpentry = rp_find(esaddr.src_addr);
if (!rpentry)
continue;
for (rp_grp = rpentry->cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
for (grp = rp_grp->grplink; grp; grp = grp->rpnext) {
if (grp->grp_route)
VIFM_CLR(vifi, grp->grp_route->pruned_oifs);
for (mrt = grp->mrtlink; mrt; mrt = mrt->grpnext)
VIFM_CLR(vifi, mrt->pruned_oifs);
}
}
}
data += (num_p_srcs) * sizeof(pim_encod_src_addr_t);
}
/*
* Start processing the groups. If this is (*,*,RP), skip it, but process
* it at the end.
*/
data = data_start;
num_groups_tmp = num_groups;
while (num_groups_tmp--) {
GET_EGADDR(&egaddr, data);
GET_HOSTSHORT(num_j_srcs, data);
GET_HOSTSHORT(num_p_srcs, data);
group = egaddr.mcast_addr;
if (!IN_MULTICAST(ntohl(group))) {
data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
continue; /* Ignore this group and jump to the next one */
}
if ((ntohl(group) == CLASSD_PREFIX)
&& (egaddr.masklen == STAR_STAR_RP_MSKLEN)) {
/* This is (*,*,RP). Jump to the next group. */
data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
continue;
}
rpentry = rp_match(group);
if (!rpentry) {
data += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
continue;
}
data_group_j_start = data;
data_group_p_start = data + num_j_srcs * sizeof(pim_encod_src_addr_t);
data_group_end = data + (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
/* Scan the Join part for (*,G) Join and then clear the
* particular interface from pruned_oifs for all (S,G).
* If the RP address in the Join message is different from
* the local match, ignore the whole group.
*/
num_j_srcs_tmp = num_j_srcs;
ignore_group = FALSE;
while (num_j_srcs_tmp--) {
GET_ESADDR(&esaddr, data);
if ((esaddr.flags & USADDR_RP_BIT) && (esaddr.flags & USADDR_WC_BIT)) {
/* This is the RP address, i.e. (*,G) Join.
* Check if the RP-mapping is consistent and if "yes",
* then Reset the pruned_oifs for all (S,G) entries.
*/
if (rpentry->address != esaddr.src_addr) {
ignore_group = TRUE;
break;
}
mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
if (mrt) {
for (mrt_srcs = mrt->group->mrtlink;
mrt_srcs;
mrt_srcs = mrt_srcs->grpnext)
VIFM_CLR(vifi, mrt_srcs->pruned_oifs);
}
break;
}
}
if (ignore_group == TRUE) {
data += (num_j_srcs_tmp + num_p_srcs) * sizeof(pim_encod_src_addr_t);
continue;
}
data = data_group_p_start;
/* Process the Prune part first */
while (num_p_srcs--) {
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) {
/* (S,G) prune sent toward S */
mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
if (!mrt)
continue; /* I don't have (S,G) to prune. Ignore. */
/* If the link is point-to-point, timeout the oif
* immediately, otherwise decrease the timer to allow
* other downstream routers to override the prune.
*/
/* TODO: XXX: increase the entry timer? */
if (v->uv_flags & VIFF_POINT_TO_POINT) {
FIRE_TIMER(mrt->vif_timers[vifi]);
} else {
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
SET_TIMER(mrt->vif_timers[vifi],
mrt->vif_deletion_delay[vifi]);
}
IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
VIFM_CLR(vifi, mrt->joined_oifs);
VIFM_SET(vifi, mrt->pruned_oifs);
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
continue;
}
if ((s_flags & USADDR_RP_BIT) && (!(s_flags & USADDR_WC_BIT))) {
/* ~(S,G)RPbit prune sent toward the RP */
mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
if (mrt) {
SET_TIMER(mrt->timer, holdtime);
if (v->uv_flags & VIFF_POINT_TO_POINT) {
FIRE_TIMER(mrt->vif_timers[vifi]);
} else {
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
SET_TIMER(mrt->vif_timers[vifi],
mrt->vif_deletion_delay[vifi]);
}
IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
VIFM_CLR(vifi, mrt->joined_oifs);
VIFM_SET(vifi, mrt->pruned_oifs);
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
continue;
}
/* There is no (S,G) entry. Check for (*,G) or (*,*,RP) */
mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE);
if (mrt) {
mrt = find_route(source, group, MRTF_SG | MRTF_RP, CREATE);
if (!mrt)
continue;
mrt->flags &= ~MRTF_NEW;
RESET_TIMER(mrt->vif_timers[vifi]);
/* TODO: XXX: The spec doens't say what value to use for
* the entry time. Use the J/P holdtime.
*/
SET_TIMER(mrt->timer, holdtime);
/* TODO: XXX: The spec says to delete the oif. However,
* its timer only should be lowered, so the prune can be
* overwritten on multiaccess LAN. Spec BUG.
*/
VIFM_CLR(vifi, mrt->joined_oifs);
VIFM_SET(vifi, mrt->pruned_oifs);
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
continue;
}
if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) {
/* (*,G) Prune */
mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE);
if (mrt) {
if (mrt->flags & MRTF_WC) {
/* TODO: XXX: Should check the whole Prune list in
* advance for (*,G) prune and if the RP address
* does not match the local RP-map, then ignore the
* whole group, not only this particular (*,G) prune.
*/
if (mrt->group->active_rp_grp->rp->rpentry->address != source)
continue; /* The RP address doesn't match. */
if (v->uv_flags & VIFF_POINT_TO_POINT) {
FIRE_TIMER(mrt->vif_timers[vifi]);
} else {
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
SET_TIMER(mrt->vif_timers[vifi],
mrt->vif_deletion_delay[vifi]);
}
IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
VIFM_CLR(vifi, mrt->joined_oifs);
VIFM_SET(vifi, mrt->pruned_oifs);
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
continue;
}
/* No (*,G) entry, but found (*,*,RP). Create (*,G) */
if (mrt->source->address != source)
continue; /* The RP address doesn't match. */
mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
if (!mrt)
continue;
mrt->flags &= ~MRTF_NEW;
RESET_TIMER(mrt->vif_timers[vifi]);
/* TODO: XXX: should only lower the oif timer, so it can
* be overwritten on multiaccess LAN. Spec bug.
*/
VIFM_CLR(vifi, mrt->joined_oifs);
VIFM_SET(vifi, mrt->pruned_oifs);
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
} /* (*,G) or (*,*,RP) found */
} /* (*,G) prune */
} /* while (num_p_srcs--) */
/* End of (S,G) and (*,G) Prune handling */
/* Jump back to the Join part and process it */
data = data_group_j_start;
while (num_j_srcs--) {
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
MASKLEN_TO_MASK(esaddr.masklen, s_mask);
if ((s_flags & USADDR_WC_BIT) && (s_flags & USADDR_RP_BIT)) {
/* (*,G) Join toward RP */
/* It has been checked already that this RP address is
* the same as the local RP-maping.
*/
mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
if (!mrt)
continue;
new_join = (VIFM_ISSET(vifi, mrt->joined_oifs) == 0);
VIFM_SET(vifi, mrt->joined_oifs);
VIFM_CLR(vifi, mrt->pruned_oifs);
VIFM_CLR(vifi, mrt->asserted_oifs);
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->vif_timers[vifi] < holdtime) {
SET_TIMER(mrt->vif_timers[vifi], holdtime);
mrt->vif_deletion_delay[vifi] = holdtime/3;
}
if (mrt->timer < holdtime)
SET_TIMER(mrt->timer, holdtime);
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
if (mrt->flags & MRTF_NEW) {
mrt->flags &= ~MRTF_NEW;
if (mrt->upstream)
send_pim_join(mrt->upstream, mrt, MRTF_RP | MRTF_WC,
PIM_JOIN_PRUNE_HOLDTIME);
}
/* Need to update the (S,G) entries, because of the previous
* cleaning of the pruned_oifs. The reason is that if the
* oifs for (*,G) weren't changed, the (S,G) entries won't
* be updated by change_interfaces()
*/
for (mrt_srcs = mrt->group->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext) {
if (new_join) {
if (mrt_srcs->upstream)
send_pim_join(mrt_srcs->upstream, mrt_srcs, MRTF_SG, PIM_JOIN_PRUNE_HOLDTIME);
VIFM_SET(vifi, mrt_srcs->joined_oifs);
VIFM_CLR(vifi, mrt_srcs->pruned_oifs);
VIFM_CLR(vifi, mrt_srcs->asserted_oifs);
change_interfaces(mrt_srcs,
mrt_srcs->incoming,
mrt_srcs->joined_oifs,
mrt_srcs->pruned_oifs,
mrt_srcs->leaves,
mrt_srcs->asserted_oifs, 0);
add_kernel_cache(mrt_srcs, mrt_srcs->source->address, mrt_srcs->group->group,
MFC_MOVE_FORCE);
mrt_srcs->flags |= MRTF_SPT;
k_chg_mfc(igmp_socket, mrt_srcs->source->address, mrt_srcs->group->group,
mrt_srcs->incoming, mrt_srcs->oifs, mrt_srcs->source->address);
}
else {
change_interfaces(mrt_srcs,
mrt_srcs->incoming,
mrt_srcs->joined_oifs,
mrt_srcs->pruned_oifs,
mrt_srcs->leaves,
mrt_srcs->asserted_oifs, 0);
}
}
continue;
}
if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) {
/* (S,G) Join toward S */
if (vifi == get_iif(source))
continue; /* Ignore this (S,G) Join */
mrt = find_route(source, group, MRTF_SG, CREATE);
if (!mrt)
continue;
new_join = (VIFM_ISSET(vifi, mrt->joined_oifs) == 0);
VIFM_SET(vifi, mrt->joined_oifs);
VIFM_CLR(vifi, mrt->pruned_oifs);
VIFM_CLR(vifi, mrt->asserted_oifs);
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->vif_timers[vifi] < holdtime) {
SET_TIMER(mrt->vif_timers[vifi], holdtime);
mrt->vif_deletion_delay[vifi] = holdtime/3;
}
if (mrt->timer < holdtime)
SET_TIMER(mrt->timer, holdtime);
/* If this is a new entry, send immediately the
* Join message toward S.
*/
if (mrt->flags & MRTF_NEW) {
send_pim_join(mrt->upstream, mrt, MRTF_SG, PIM_JOIN_PRUNE_HOLDTIME);
mrt->flags &= ~MRTF_NEW;
}
/* Note that we must create (S,G) without the RPbit set.
* If we already had such entry, change_interfaces() will
* reset the RPbit propertly.
*/
change_interfaces(mrt,
mrt->source->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
/* If this is join from new interface and we have incoming data
* start forwarding immediately.
*/
if (new_join) {
add_kernel_cache(mrt, mrt->source->address, mrt->group->group, MFC_MOVE_FORCE);
k_chg_mfc(igmp_socket, mrt->source->address, mrt->group->group,
mrt->incoming, mrt->oifs, mrt->source->address);
}
continue;
}
} /* while (num_j_srcs--) */
data = data_group_end;
} /* for all groups */
/* Now process the (*,*,RP) Join/Prune */
if (star_star_rp_found != TRUE)
return TRUE;
data = data_start;
while (num_groups--) {
/* The conservative approach is to scan again the whole message,
* just in case if we have more than one (*,*,RP) requests.
*/
GET_EGADDR(&egaddr, data);
GET_HOSTSHORT(num_j_srcs, data);
GET_HOSTSHORT(num_p_srcs, data);
group = egaddr.mcast_addr;
if ((ntohl(group) != CLASSD_PREFIX)
|| (egaddr.masklen != STAR_STAR_RP_MSKLEN)) {
/* This is not (*,*,RP). Jump to the next group. */
data +=
(num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
continue;
}
/* (*,*,RP) found */
while (num_j_srcs--) {
/* TODO: XXX: check that the iif is different from the Join oifs */
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
MASKLEN_TO_MASK(esaddr.masklen, s_mask);
mrt = find_route(source, INADDR_ANY_N, MRTF_PMBR, CREATE);
if (!mrt)
continue;
VIFM_SET(vifi, mrt->joined_oifs);
VIFM_CLR(vifi, mrt->pruned_oifs);
VIFM_CLR(vifi, mrt->asserted_oifs);
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->vif_timers[vifi] < holdtime) {
SET_TIMER(mrt->vif_timers[vifi], holdtime);
mrt->vif_deletion_delay[vifi] = holdtime/3;
}
if (mrt->timer < holdtime)
SET_TIMER(mrt->timer, holdtime);
mrt->flags &= ~MRTF_NEW;
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
/* Need to update the (S,G) and (*,G) entries, because of
* the previous cleaning of the pruned_oifs. The reason is
* that if the oifs for (*,*,RP) weren't changed, the
* (*,G) and (S,G) entries won't be updated by change_interfaces()
*/
for (rp_grp = mrt->source->cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
for (grp = rp_grp->grplink; grp; grp = grp->rpnext) {
/* Update the (*,G) entry */
if (grp->grp_route) {
change_interfaces(grp->grp_route,
grp->grp_route->incoming,
grp->grp_route->joined_oifs,
grp->grp_route->pruned_oifs,
grp->grp_route->leaves,
grp->grp_route->asserted_oifs, 0);
}
/* Update the (S,G) entries */
for (mrt_srcs = grp->mrtlink; mrt_srcs; mrt_srcs = mrt_srcs->grpnext)
change_interfaces(mrt_srcs,
mrt_srcs->incoming,
mrt_srcs->joined_oifs,
mrt_srcs->pruned_oifs,
mrt_srcs->leaves,
mrt_srcs->asserted_oifs, 0);
}
}
continue;
}
while (num_p_srcs--) {
/* TODO: XXX: can we have (*,*,RP) Prune? */
GET_ESADDR(&esaddr, data);
source = esaddr.src_addr;
if (!inet_valid_host(source))
continue;
s_flags = esaddr.flags;
MASKLEN_TO_MASK(esaddr.masklen, s_mask);
mrt = find_route(source, INADDR_ANY_N, MRTF_PMBR, DONT_CREATE);
if (!mrt)
continue;
/* If the link is point-to-point, timeout the oif
* immediately, otherwise decrease the timer to allow
* other downstream routers to override the prune.
*/
/* TODO: XXX: increase the entry timer? */
if (v->uv_flags & VIFF_POINT_TO_POINT) {
FIRE_TIMER(mrt->vif_timers[vifi]);
} else {
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->vif_timers[vifi] > mrt->vif_deletion_delay[vifi])
SET_TIMER(mrt->vif_timers[vifi],
mrt->vif_deletion_delay[vifi]);
}
IF_TIMER_NOT_SET(mrt->vif_timers[vifi]) {
VIFM_CLR(vifi, mrt->joined_oifs);
VIFM_SET(vifi, mrt->pruned_oifs);
VIFM_SET(vifi, mrt->asserted_oifs);
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
}
}
} /* For all groups processing (*,*,R) */
return TRUE;
}
/*
* Function for sending single PIM-JOIN instantly.
*/
void send_pim_join(pim_nbr_entry_t *pim_nbr, mrtentry_t *mrt, uint16_t flags, uint16_t holdtime)
{
if (pim_nbr == NULL)
return;
if (flags & MRTF_SG)
add_jp_entry(pim_nbr, holdtime, mrt->group->group,
SINGLE_GRP_MSKLEN, mrt->source->address,
SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN);
else
add_jp_entry(pim_nbr, holdtime, mrt->group->group,
SINGLE_GRP_MSKLEN, mrt->group->rpaddr,
SINGLE_SRC_MSKLEN, flags, PIM_ACTION_JOIN);
pack_and_send_jp_message(pim_nbr);
}
/*
* TODO: NOT USED, probably buggy, but may need it in the future.
*/
/*
* TODO: create two functions: periodic which timeout the timers
* and non-periodic which only check but don't timeout the timers.
*/
/*
* Create and send Join/Prune messages per interface.
* Only the entries which have the Join/Prune timer expired are included.
* In the special case when we have ~(S,G)RPbit Prune entry, we must
* include any (*,G) or (*,*,RP)
* Currently the whole table is scanned. In the future will have all
* routing entries linked in a chain with the corresponding upstream
* pim_nbr_entry.
*
* If pim_nbr is not NULL, then send to only this particular PIM neighbor,
*/
int send_periodic_pim_join_prune(vifi_t vifi, pim_nbr_entry_t *pim_nbr, uint16_t holdtime)
{
grpentry_t *grp;
mrtentry_t *mrt;
rpentry_t *rp;
uint32_t addr;
struct uvif *v;
pim_nbr_entry_t *nbr;
cand_rp_t *cand_rp;
/* Walk through all routing entries. The iif must match to include the
* entry. Check first the (*,G) entry and then all associated (S,G).
* At the end of the message will add any (*,*,RP) entries.
* TODO: check other PIM-SM implementations and decide the more
* appropriate place to put the (*,*,RP) entries: in the beginning of the
* message or at the end.
*/
v = &uvifs[vifi];
/* Check the (*,G) and (S,G) entries */
for (grp = grplist; grp; grp = grp->next) {
mrt = grp->grp_route;
/* TODO: XXX: TIMER implem. dependency! */
if (mrt && (mrt->incoming == vifi) && (mrt->jp_timer <= TIMER_INTERVAL)) {
/* If join/prune to a particular neighbor only was specified */
if (pim_nbr && mrt->upstream != pim_nbr)
continue;
/* Don't send (*,G) or (S,G,rpt) Join/Prune */
/* TODO: this handles (S,G,rpt) Join/Prune? */
if (!(mrt->flags & MRTF_SG) && IN_PIM_SSM_RANGE(grp->group)) {
logit(LOG_DEBUG, 0, "Skip j/p for SSM (!SG)");
continue;
}
/* TODO: XXX: The J/P suppression timer is not in the spec! */
if (!VIFM_ISEMPTY(mrt->joined_oifs) || (v->uv_flags & VIFF_DR)) {
add_jp_entry(mrt->upstream, holdtime,
grp->group,
SINGLE_GRP_MSKLEN,
grp->rpaddr,
SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN);
}
/* TODO: XXX: TIMER implem. dependency! */
if (VIFM_ISEMPTY(mrt->joined_oifs)
&& (!(v->uv_flags & VIFF_DR))
&& (mrt->jp_timer <= TIMER_INTERVAL)) {
add_jp_entry(mrt->upstream, holdtime,
grp->group, SINGLE_GRP_MSKLEN,
grp->rpaddr,
SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE);
}
}
/* Check the (S,G) entries */
for (mrt = grp->mrtlink; mrt; mrt = mrt->grpnext) {
/* If join/prune to a particular neighbor only was specified */
if (pim_nbr && mrt->upstream != pim_nbr)
continue;
if (mrt->flags & MRTF_RP) {
/* RPbit set */
addr = mrt->source->address;
if (VIFM_ISEMPTY(mrt->joined_oifs) || find_vif_direct_local(addr) != NO_VIF) {
/* TODO: XXX: TIMER implem. dependency! */
if (grp->grp_route &&
grp->grp_route->incoming == vifi &&
grp->grp_route->jp_timer <= TIMER_INTERVAL)
/* S is directly connected. Send toward RP */
add_jp_entry(grp->grp_route->upstream,
holdtime,
grp->group, SINGLE_GRP_MSKLEN,
addr, SINGLE_SRC_MSKLEN,
MRTF_RP, PIM_ACTION_PRUNE);
}
}
else {
/* RPbit cleared */
if (VIFM_ISEMPTY(mrt->joined_oifs)) {
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->incoming == vifi && mrt->jp_timer <= TIMER_INTERVAL)
add_jp_entry(mrt->upstream, holdtime,
grp->group, SINGLE_GRP_MSKLEN,
mrt->source->address,
SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE);
} else {
logit(LOG_DEBUG, 0 , "Joined not empty, group %s",
inet_ntoa(*(struct in_addr *)&grp->group));
/* TODO: XXX: TIMER implem. dependency! */
if (mrt->incoming == vifi && mrt->jp_timer <= TIMER_INTERVAL)
add_jp_entry(mrt->upstream, holdtime,
grp->group, SINGLE_GRP_MSKLEN,
mrt->source->address,
SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN);
}
/* TODO: XXX: TIMER implem. dependency! */
if ((mrt->flags & MRTF_SPT) &&
grp->grp_route &&
mrt->incoming != grp->grp_route->incoming &&
grp->grp_route->incoming == vifi &&
grp->grp_route->jp_timer <= TIMER_INTERVAL)
add_jp_entry(grp->grp_route->upstream, holdtime,
grp->group, SINGLE_GRP_MSKLEN,
mrt->source->address,
SINGLE_SRC_MSKLEN, MRTF_RP,
PIM_ACTION_PRUNE);
}
}
}
/* Check the (*,*,RP) entries */
for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) {
rp = cand_rp->rpentry;
/* If join/prune to a particular neighbor only was specified */
if (pim_nbr && rp->upstream != pim_nbr)
continue;
/* TODO: XXX: TIMER implem. dependency! */
if (rp->mrtlink &&
rp->incoming == vifi &&
rp->mrtlink->jp_timer <= TIMER_INTERVAL) {
add_jp_entry(rp->upstream, holdtime, htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN,
rp->address, SINGLE_SRC_MSKLEN, MRTF_RP | MRTF_WC, PIM_ACTION_JOIN);
}
}
/* Send all pending Join/Prune messages */
for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next) {
/* If join/prune to a particular neighbor only was specified */
if (pim_nbr && (nbr != pim_nbr))
continue;
pack_and_send_jp_message(nbr);
}
return TRUE;
}
int add_jp_entry(pim_nbr_entry_t *pim_nbr, uint16_t holdtime, uint32_t group,
uint8_t grp_msklen, uint32_t source, uint8_t src_msklen,
uint16_t addr_flags, uint8_t join_prune)
{
build_jp_message_t *bjpm;
uint8_t *data;
uint8_t flags = 0;
uint32_t jp_message_size;
int rp_flag;
int new_grp = FALSE;
bjpm = pim_nbr->build_jp_message;
if (group == htonl(CLASSD_PREFIX) && grp_msklen == STAR_STAR_RP_MSKLEN) {
rp_flag = TRUE;
} else {
rp_flag = FALSE;
if (bjpm) {
if ((bjpm->curr_group != group)
|| (bjpm->curr_group_msklen != grp_msklen)
|| (bjpm->holdtime != holdtime)) {
new_grp = TRUE;
}
}
}
if (bjpm) {
if (new_grp == TRUE)
pack_jp_message_grp(pim_nbr);
/* Check if we have already 254 groups. */
if (*bjpm->num_groups_ptr == ((uint8_t)~0 - 1)) {
pack_and_send_jp_message(pim_nbr);
bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */
}
}
if (bjpm) {
jp_message_size = bjpm->jp_message_size;
/* sizeof(pim_jp_encod_grp_t) is used to precalculate the size. */
if (bjpm->join_list_size + bjpm->prune_list_size) {
jp_message_size += sizeof(pim_jp_encod_grp_t);
jp_message_size += bjpm->join_list_size;
jp_message_size += bjpm->prune_list_size;
} else if (new_grp == TRUE) {
jp_message_size += sizeof(pim_jp_encod_grp_t);
}
/* sizeof(pim_jp_encod_grp_t) is used to precalculate the size. */
if (bjpm->rp_list_join_size + bjpm->rp_list_prune_size) {
jp_message_size += sizeof(pim_jp_encod_grp_t);
jp_message_size += bjpm->rp_list_join_size;
jp_message_size += bjpm->rp_list_prune_size;
} else if (rp_flag == TRUE) {
jp_message_size += sizeof(pim_jp_encod_grp_t);
}
/* Check also would the new entry push us over the limit. */
jp_message_size += sizeof(pim_encod_src_addr_t);
/* TODO: Should check the jp_message_size also against MTU. */
if (jp_message_size > MAX_JP_MESSAGE_SIZE) {
pack_and_send_jp_message(pim_nbr);
bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */
}
}
if (!bjpm) {
bjpm = get_jp_working_buff();
if (!bjpm) {
logit(LOG_ERR, 0, "Failed allocating working buffer in add_jp_entry()");
exit (-1);
}
pim_nbr->build_jp_message = bjpm;
data = bjpm->jp_message;
PUT_EUADDR(pim_nbr->address, data);
PUT_BYTE(0, data); /* Reserved */
bjpm->num_groups_ptr = data++; /* The pointer for numgroups */
*(bjpm->num_groups_ptr) = 0; /* Zero groups */
PUT_HOSTSHORT(holdtime, data);
bjpm->holdtime = holdtime;
bjpm->jp_message_size = data - bjpm->jp_message;
if (rp_flag == FALSE)
new_grp = TRUE;
}
if (new_grp == TRUE) {
bjpm->curr_group = group;
bjpm->curr_group_msklen = grp_msklen;
}
switch (join_prune) {
case PIM_ACTION_JOIN:
if (rp_flag == TRUE)
data = bjpm->rp_list_join + bjpm->rp_list_join_size;
else
data = bjpm->join_list + bjpm->join_list_size;
break;
case PIM_ACTION_PRUNE:
if (rp_flag == TRUE)
data = bjpm->rp_list_prune + bjpm->rp_list_prune_size;
else
data = bjpm->prune_list + bjpm->prune_list_size;
break;
default:
return FALSE;
}
flags |= USADDR_S_BIT; /* Mandatory for PIMv2 */
if (addr_flags & MRTF_RP)
flags |= USADDR_RP_BIT;
if (addr_flags & MRTF_WC)
flags |= USADDR_WC_BIT;
PUT_ESADDR(source, src_msklen, flags, data);
switch (join_prune) {
case PIM_ACTION_JOIN:
if (rp_flag == TRUE) {
bjpm->rp_list_join_size = data - bjpm->rp_list_join;
bjpm->rp_list_join_number++;
} else {
bjpm->join_list_size = data - bjpm->join_list;
bjpm->join_addr_number++;
}
break;
case PIM_ACTION_PRUNE:
if (rp_flag == TRUE) {
bjpm->rp_list_prune_size = data - bjpm->rp_list_prune;
bjpm->rp_list_prune_number++;
} else {
bjpm->prune_list_size = data - bjpm->prune_list;
bjpm->prune_addr_number++;
}
break;
default:
return FALSE;
}
return TRUE;
}
static build_jp_message_t *get_jp_working_buff(void)
{
build_jp_message_t *bjpm;
if (build_jp_message_pool_counter == 0) {
bjpm = calloc(1, sizeof(build_jp_message_t));
if (!bjpm)
return NULL;
bjpm->next = NULL;
bjpm->jp_message_size = 0;
bjpm->jp_message = calloc(1, MAX_JP_MESSAGE_SIZE + sizeof(pim_jp_header_t));
if (!bjpm->jp_message) {
free(bjpm);
return NULL;
}
bjpm->join_list_size = 0;
bjpm->join_addr_number = 0;
bjpm->join_list = calloc(1, MAX_JP_MESSAGE_SIZE - sizeof(pim_jp_encod_grp_t));
if (!bjpm->join_list) {
free(bjpm->jp_message);
free(bjpm);
return NULL;
}
bjpm->prune_list_size = 0;
bjpm->prune_addr_number = 0;
bjpm->prune_list = calloc(1, MAX_JP_MESSAGE_SIZE - sizeof(pim_jp_encod_grp_t));
if (!bjpm->prune_list) {
free(bjpm->join_list);
free(bjpm->jp_message);
free(bjpm);
return NULL;
}
bjpm->rp_list_join_size = 0;
bjpm->rp_list_join_number = 0;
bjpm->rp_list_join = calloc(1, MAX_JP_MESSAGE_SIZE - sizeof(pim_jp_encod_grp_t));
if (!bjpm->rp_list_join) {
free(bjpm->prune_list);
free(bjpm->join_list);
free(bjpm->jp_message);
free(bjpm);
return NULL;
}
bjpm->rp_list_prune_size = 0;
bjpm->rp_list_prune_number = 0;
bjpm->rp_list_prune = calloc(1, MAX_JP_MESSAGE_SIZE - sizeof(pim_jp_encod_grp_t));
if (!bjpm->rp_list_prune) {
free(bjpm->rp_list_join);
free(bjpm->prune_list);
free(bjpm->join_list);
free(bjpm->jp_message);
free(bjpm);
return NULL;
}
bjpm->curr_group = INADDR_ANY_N;
bjpm->curr_group_msklen = 0;
bjpm->holdtime = 0;
return bjpm;
}
bjpm = build_jp_message_pool;
build_jp_message_pool = build_jp_message_pool->next;
build_jp_message_pool_counter--;
bjpm->jp_message_size = 0;
bjpm->join_list_size = 0;
bjpm->join_addr_number = 0;
bjpm->prune_list_size = 0;
bjpm->prune_addr_number = 0;
bjpm->curr_group = INADDR_ANY_N;
bjpm->curr_group_msklen = 0;
return bjpm;
}
static void return_jp_working_buff(pim_nbr_entry_t *pim_nbr)
{
build_jp_message_t *bjpm = pim_nbr->build_jp_message;
if (!bjpm)
return;
/* Don't waste memory by keeping too many free buffers */
/* TODO: check/modify the definitions for POOL_NUMBER and size */
if (build_jp_message_pool_counter >= MAX_JP_MESSAGE_POOL_NUMBER) {
free(bjpm->jp_message);
free(bjpm->join_list);
free(bjpm->prune_list);
free(bjpm->rp_list_join);
free(bjpm->rp_list_prune);
free(bjpm);
} else {
bjpm->next = build_jp_message_pool;
build_jp_message_pool = bjpm;
build_jp_message_pool_counter++;
}
pim_nbr->build_jp_message = NULL;
}
static void pack_jp_message_grp(pim_nbr_entry_t *pim_nbr)
{
build_jp_message_t *bjpm;
uint8_t *data;
bjpm = pim_nbr->build_jp_message;
if (!bjpm)
return;
if (bjpm->join_list_size + bjpm->prune_list_size) {
data = bjpm->jp_message + bjpm->jp_message_size;
PUT_EGADDR(bjpm->curr_group, bjpm->curr_group_msklen, 0, data);
PUT_HOSTSHORT(bjpm->join_addr_number, data);
PUT_HOSTSHORT(bjpm->prune_addr_number, data);
memcpy(data, bjpm->join_list, bjpm->join_list_size);
data += bjpm->join_list_size;
memcpy(data, bjpm->prune_list, bjpm->prune_list_size);
data += bjpm->prune_list_size;
bjpm->jp_message_size = (data - bjpm->jp_message);
bjpm->curr_group = INADDR_ANY_N;
bjpm->curr_group_msklen = 0;
bjpm->join_list_size = 0;
bjpm->join_addr_number = 0;
bjpm->prune_list_size = 0;
bjpm->prune_addr_number = 0;
(*bjpm->num_groups_ptr)++;
}
}
static void pack_jp_message_rp(pim_nbr_entry_t *pim_nbr)
{
build_jp_message_t *bjpm;
uint8_t *data;
bjpm = pim_nbr->build_jp_message;
if (!bjpm)
return;
if (bjpm->rp_list_join_size + bjpm->rp_list_prune_size) {
data = bjpm->jp_message + bjpm->jp_message_size;
PUT_EGADDR(htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, 0, data);
PUT_HOSTSHORT(bjpm->rp_list_join_number, data);
PUT_HOSTSHORT(bjpm->rp_list_prune_number, data);
memcpy(data, bjpm->rp_list_join, bjpm->rp_list_join_size);
data += bjpm->rp_list_join_size;
memcpy(data, bjpm->rp_list_prune, bjpm->rp_list_prune_size);
data += bjpm->rp_list_prune_size;
bjpm->jp_message_size = (data - bjpm->jp_message);
bjpm->rp_list_join_size = 0;
bjpm->rp_list_join_number = 0;
bjpm->rp_list_prune_size = 0;
bjpm->rp_list_prune_number = 0;
(*bjpm->num_groups_ptr)++;
}
}
void pack_and_send_jp_message(pim_nbr_entry_t *pim_nbr)
{
if (!pim_nbr)
return;
pack_jp_message_grp(pim_nbr);
pack_jp_message_rp(pim_nbr);
send_jp_message(pim_nbr);
}
static void send_jp_message(pim_nbr_entry_t *pim_nbr)
{
build_jp_message_t *bjpm;
vifi_t vifi;
bjpm = pim_nbr->build_jp_message;
if (!bjpm)
return;
vifi = pim_nbr->vifi;
memcpy(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t),
bjpm->jp_message, bjpm->jp_message_size);
logit(LOG_INFO, 0, "Send PIM JOIN/PRUNE from %s on %s",
inet_fmt(uvifs[vifi].uv_lcl_addr, s1, sizeof(s1)), uvifs[vifi].uv_name);
send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
PIM_JOIN_PRUNE, bjpm->jp_message_size);
return_jp_working_buff(pim_nbr);
}
/************************************************************************
* PIM_ASSERT
************************************************************************/
int receive_pim_assert(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
{
vifi_t vifi;
pim_encod_uni_addr_t eusaddr;
pim_encod_grp_addr_t egaddr;
uint32_t source, group;
mrtentry_t *mrt, *mrt2;
uint8_t *data;
struct uvif *v;
uint32_t assert_preference;
uint32_t assert_metric;
uint32_t assert_rptbit;
uint32_t local_metric;
uint32_t local_preference;
uint8_t local_rptbit;
uint8_t local_wins;
pim_nbr_entry_t *original_upstream_router;
vifi = find_vif_direct(src);
if (vifi == NO_VIF) {
/* Either a local vif or somehow received PIM_ASSERT from
* non-directly connected router. Ignore it.
*/
if (local_address(src) == NO_VIF)
logit(LOG_DEBUG, 0, "Ignoring PIM_ASSERT from non-neighbor router %s",
inet_fmt(src, s1, sizeof(s1)));
return FALSE;
}
/* Checksum */
if (inet_cksum((uint16_t *)msg, len))
return FALSE;
v = &uvifs[vifi];
if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER))
return FALSE; /* Shoudn't come on this interface */
data = (uint8_t *)(msg + sizeof(pim_header_t));
/* Get the group and source addresses */
GET_EGADDR(&egaddr, data);
GET_EUADDR(&eusaddr, data);
/* Get the metric related info */
GET_HOSTLONG(assert_preference, data);
GET_HOSTLONG(assert_metric, data);
assert_rptbit = assert_preference & PIM_ASSERT_RPT_BIT;
source = eusaddr.unicast_addr;
group = egaddr.mcast_addr;
logit(LOG_INFO, 0, "Received PIM ASSERT from %s for group %s and source %s",
inet_fmt(src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)),
inet_fmt(source, s3, sizeof(s3)));
/* Find the longest "active" entry, i.e. the one with a kernel mirror */
if (assert_rptbit) {
mrt = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE);
if (mrt && !(mrt->flags & MRTF_KERNEL_CACHE)) {
if (mrt->flags & MRTF_WC)
mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
}
} else {
mrt = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
if (mrt && !(mrt->flags & MRTF_KERNEL_CACHE)) {
if (mrt->flags & MRTF_SG) {
mrt2 = mrt->group->grp_route;
if (mrt2 && (mrt2->flags & MRTF_KERNEL_CACHE))
mrt = mrt2;
else
mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
} else {
if (mrt->flags & MRTF_WC)
mrt = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
}
}
}
if (!mrt || !(mrt->flags & MRTF_KERNEL_CACHE)) {
/* No routing entry or not "active" entry. Ignore the assert */
return FALSE;
}
/* Prepare the local preference and metric */
if ((mrt->flags & MRTF_PMBR)
|| ((mrt->flags & MRTF_SG)
&& !(mrt->flags & MRTF_RP))) {
/* Either (S,G) (toward S) or (*,*,RP). */
/* TODO: XXX: get the info from mrt, or source or from kernel ? */
/*
local_metric = mrt->source->metric;
local_preference = mrt->source->preference;
*/
local_metric = mrt->metric;
local_preference = mrt->preference;
} else {
/* Should be (*,G) or (S,G)RPbit entry.
* Get what we need from the RP info.
*/
/* TODO: get the info from mrt, RP-entry or kernel? */
/*
local_metric =
mrt->group->active_rp_grp->rp->rpentry->metric;
local_preference =
mrt->group->active_rp_grp->rp->rpentry->preference;
*/
local_metric = mrt->metric;
local_preference = mrt->preference;
}
local_rptbit = (mrt->flags & MRTF_RP);
if (local_rptbit) {
/* Make the RPT bit the most significant one */
local_preference |= PIM_ASSERT_RPT_BIT;
}
if (VIFM_ISSET(vifi, mrt->oifs)) {
/* The ASSERT has arrived on oif */
/* TODO: XXX: here the processing order is different from the spec.
* The spec requires first eventually to create a routing entry
* (see 3.5.2.1(1) and then compare the metrics. Here we compare
* first the metrics with the existing longest match entry and
* if we lose then create a new entry and compare again. This saves
* us the unnecessary creating of a routing entry if we anyway are
* going to lose: for example the local (*,*,RP) vs the remote
* (*,*,RP) or (*,G)
*/
local_wins = compare_metrics(local_preference, local_metric,
v->uv_lcl_addr, assert_preference,
assert_metric, src);
if (local_wins == TRUE) {
/* TODO: verify the parameters */
send_pim_assert(source, group, vifi, mrt);
return TRUE;
}
/* Create a "better" routing entry and try again */
if (assert_rptbit && (mrt->flags & MRTF_PMBR)) {
/* The matching entry was (*,*,RP). Create (*,G) */
mrt2 = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
} else if (!assert_rptbit && (mrt->flags & (MRTF_WC | MRTF_PMBR))) {
/* create (S,G) */
mrt2 = find_route(source, group, MRTF_SG, CREATE);
} else {
/* We have no chance to win. Give up and prune the oif */
mrt2 = NULL;
}
if (mrt2 && (mrt2->flags & MRTF_NEW)) {
mrt2->flags &= ~MRTF_NEW;
/* TODO: XXX: The spec doesn't say what entry timer value
* to use when the routing entry is created because of asserts.
*/
SET_TIMER(mrt2->timer, PIM_DATA_TIMEOUT);
if (mrt2->flags & MRTF_RP) {
/* Either (*,G) or (S,G)RPbit entry.
* Get what we need from the RP info.
*/
/* TODO: where to get the metric+preference from? */
/*
local_metric =
mrt->group->active_rp_grp->rp->rpentry->metric;
local_preference =
mrt->group->active_rp_grp->rp->rpentry->preference;
*/
local_metric = mrt->metric;
local_preference = mrt->preference;
local_preference |= PIM_ASSERT_RPT_BIT;
} else {
/* (S,G) toward the source */
/* TODO: where to get the metric from ? */
/*
local_metric = mrt->source->metric;
local_preference = mrt->source->preference;
*/
local_metric = mrt->metric;
local_preference = mrt->preference;
}
local_wins = compare_metrics(local_preference, local_metric,
v->uv_lcl_addr, assert_preference,
assert_metric, src);
if (local_wins == TRUE) {
/* TODO: verify the parameters */
send_pim_assert(source, group, vifi, mrt);
return TRUE;
}
/* We lost, but have created the entry which has to be pruned */
mrt = mrt2;
}
/* Have to remove that outgoing vifi from mrt */
VIFM_SET(vifi, mrt->asserted_oifs);
mrt->flags |= MRTF_ASSERTED;
if (mrt->assert_timer < PIM_ASSERT_TIMEOUT)
SET_TIMER(mrt->assert_timer, PIM_ASSERT_TIMEOUT);
/* TODO: XXX: check that the timer of all affected routing entries
* has been restarted.
*/
change_interfaces(mrt,
mrt->incoming,
mrt->joined_oifs,
mrt->pruned_oifs,
mrt->leaves,
mrt->asserted_oifs, 0);
return FALSE; /* Doesn't matter the return value */
} /* End of assert received on oif */
if (mrt->incoming == vifi) {
/* Assert received on iif */
if (assert_rptbit) {
if (!(mrt->flags & MRTF_RP))
return TRUE; /* The locally used upstream router will
* win the assert, so don't change it.
*/
}
/* Ignore assert message if we do not have an upstream router */
if (mrt->upstream == NULL)
return FALSE;
/* TODO: where to get the local metric and preference from?
* system call or mrt is fine?
*/
local_metric = mrt->metric;
local_preference = mrt->preference;
if (mrt->flags & MRTF_RP)
local_preference |= PIM_ASSERT_RPT_BIT;
local_wins = compare_metrics(local_preference, local_metric,
mrt->upstream->address,
assert_preference, assert_metric, src);
if (local_wins == TRUE)
return TRUE; /* return whatever */
/* The upstream must be changed to the winner */
mrt->preference = assert_preference;
mrt->metric = assert_metric;
mrt->upstream = find_pim_nbr(src);
/* Check if the upstream router is different from the original one */
if (mrt->flags & MRTF_PMBR) {
original_upstream_router = mrt->source->upstream;
} else {
if (mrt->flags & MRTF_RP)
original_upstream_router = mrt->group->active_rp_grp->rp->rpentry->upstream;
else
original_upstream_router = mrt->source->upstream;
}
if (mrt->upstream != original_upstream_router) {
mrt->flags |= MRTF_ASSERTED;
SET_TIMER(mrt->assert_timer, PIM_ASSERT_TIMEOUT);
} else {
mrt->flags &= ~MRTF_ASSERTED;
}
}
return TRUE;
}
int send_pim_assert(uint32_t source, uint32_t group, vifi_t vifi, mrtentry_t *mrt)
{
uint8_t *data;
uint8_t *data_start;
uint32_t local_preference;
uint32_t local_metric;
srcentry_t *srcentry __attribute__((unused));
/* Don't send assert if the outgoing interface a tunnel or register vif */
/* TODO: XXX: in the code above asserts are accepted over VIFF_TUNNEL.
* Check if anything can go wrong if asserts are accepted and/or
* sent over VIFF_TUNNEL.
*/
if (uvifs[vifi].uv_flags & (VIFF_REGISTER | VIFF_TUNNEL))
return FALSE;
data = (uint8_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t));
data_start = data;
PUT_EGADDR(group, SINGLE_GRP_MSKLEN, 0, data);
PUT_EUADDR(source, data);
/* TODO: XXX: where to get the metric from: srcentry or mrt
* or from the kernel?
*/
if (mrt->flags & MRTF_PMBR) {
/* (*,*,RP) */
srcentry = mrt->source;
/* TODO:
set_incoming(srcentry, PIM_IIF_RP);
*/
} else if (mrt->flags & MRTF_RP) {
/* (*,G) or (S,G)RPbit (iif toward RP) */
srcentry = mrt->group->active_rp_grp->rp->rpentry;
/* TODO:
set_incoming(srcentry, PIM_IIF_RP);
*/
} else {
/* (S,G) toward S */
srcentry = mrt->source;
/* TODO:
set_incoming(srcentry, PIM_IIF_SOURCE);
*/
}
/* TODO: check again!
local_metric = srcentry->metric;
local_preference = srcentry->preference;
*/
local_metric = mrt->metric;
local_preference = mrt->preference;
if (mrt->flags & MRTF_RP)
local_preference |= PIM_ASSERT_RPT_BIT;
PUT_HOSTLONG(local_preference, data);
PUT_HOSTLONG(local_metric, data);
logit(LOG_INFO, 0, "Send PIM ASSERT from %s for group %s and source %s",
inet_fmt(uvifs[vifi].uv_lcl_addr, s1, sizeof(s1)),
inet_fmt(group, s2, sizeof(s2)),
inet_fmt(source, s3, sizeof(s3)));
send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
PIM_ASSERT, data - data_start);
return TRUE;
}
/* Return TRUE if the local win, otherwise FALSE */
static int compare_metrics(uint32_t local_preference, uint32_t local_metric, uint32_t local_address,
uint32_t remote_preference, uint32_t remote_metric, uint32_t remote_address)
{
/* Now lets see who has a smaller gun (aka "asserts war") */
/* FYI, the smaller gun...err metric wins, but if the same
* caliber, then the bigger network address wins. The order of
* threatment is: preference, metric, address.
*/
/* The RPT bits are already included as the most significant bits
* of the preferences.
*/
if (remote_preference > local_preference)
return TRUE;
if (remote_preference < local_preference)
return FALSE;
if (remote_metric > local_metric)
return TRUE;
if (remote_metric < local_metric)
return FALSE;
if (ntohl(local_address) > ntohl(remote_address))
return TRUE;
return FALSE;
}
/************************************************************************
* PIM_BOOTSTRAP
************************************************************************/
#define PIM_BOOTSTRAP_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN)
int receive_pim_bootstrap(uint32_t src, uint32_t dst, char *msg, size_t len)
{
uint8_t *data;
uint8_t *max_data;
uint16_t new_bsr_fragment_tag;
uint8_t new_bsr_hash_masklen;
uint8_t new_bsr_priority;
pim_encod_uni_addr_t new_bsr_uni_addr;
uint32_t new_bsr_address;
struct rpfctl rpfc;
pim_nbr_entry_t *n, *rpf_neighbor __attribute__((unused));
uint32_t neighbor_addr;
vifi_t vifi, incoming = NO_VIF;
int min_datalen;
pim_encod_grp_addr_t curr_group_addr;
pim_encod_uni_addr_t curr_rp_addr;
uint8_t curr_rp_count;
uint8_t curr_frag_rp_count;
uint16_t reserved_short __attribute__((unused));
uint16_t curr_rp_holdtime;
uint8_t curr_rp_priority;
uint8_t reserved_byte __attribute__((unused));
uint32_t curr_group_mask;
uint32_t prefix_h;
grp_mask_t *grp_mask;
grp_mask_t *grp_mask_next;
rp_grp_entry_t *grp_rp;
rp_grp_entry_t *grp_rp_next;
/* Checksum */
if (inet_cksum((uint16_t *)msg, len))
return FALSE;
if (find_vif_direct(src) == NO_VIF) {
/* Either a local vif or somehow received PIM_BOOTSTRAP from
* non-directly connected router. Ignore it.
*/
if (local_address(src) == NO_VIF)
logit(LOG_DEBUG, 0, "Ignoring PIM_BOOTSTRAP from non-neighbor router %s",
inet_fmt(src, s1, sizeof(s1)));
return FALSE;
}
/* sanity check for the minimum length */
if (len < PIM_BOOTSTRAP_MINLEN) {
logit(LOG_NOTICE, 0, "receive_pim_bootstrap: Bootstrap message size(%u) is too short from %s",
len, inet_fmt(src, s1, sizeof(s1)));
return FALSE;
}
data = (uint8_t *)(msg + sizeof(pim_header_t));
/* Parse the PIM_BOOTSTRAP message */
GET_HOSTSHORT(new_bsr_fragment_tag, data);
GET_BYTE(new_bsr_hash_masklen, data);
GET_BYTE(new_bsr_priority, data);
GET_EUADDR(&new_bsr_uni_addr, data);
new_bsr_address = new_bsr_uni_addr.unicast_addr;
if (local_address(new_bsr_address) != NO_VIF)
return FALSE; /* The new BSR is one of my local addresses */
/*
* Compare the current BSR priority with the priority of the BSR
* included in the message.
*/
/* TODO: if I am just starting and will become the BSR,
* I should accept the message coming from the current BSR and get the
* current Cand-RP-Set.
*/
if ((curr_bsr_priority > new_bsr_priority) ||
((curr_bsr_priority == new_bsr_priority)
&& (ntohl(curr_bsr_address) > ntohl(new_bsr_address)))) {
/* The message's BSR is less preferred than the current BSR */
return FALSE; /* Ignore the received BSR message */
}
logit(LOG_INFO, 0, "Received PIM Bootstrap candidate %s, priority %d",
inet_fmt(new_bsr_address, s1, sizeof(s1)), new_bsr_priority);
/* Check the iif, if this was PIM-ROUTERS multicast */
if (dst == allpimrouters_group) {
k_req_incoming(new_bsr_address, &rpfc);
if (rpfc.iif == NO_VIF || rpfc.rpfneighbor.s_addr == INADDR_ANY_N) {
/* coudn't find a route to the BSR */
return FALSE;
}
neighbor_addr = rpfc.rpfneighbor.s_addr;
incoming = rpfc.iif;
if (uvifs[incoming].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER))
return FALSE; /* Shoudn't arrive on that interface */
/* Find the upstream router */
for (n = uvifs[incoming].uv_pim_neighbors; n; n = n->next) {
if (ntohl(neighbor_addr) < ntohl(n->address))
continue;
if (neighbor_addr == n->address) {
rpf_neighbor = n;
break;
}
return FALSE; /* No neighbor toward BSR found */
}
if (!n || n->address != src)
return FALSE; /* Sender of this message is not the RPF neighbor */
} else {
if (local_address(dst) == NO_VIF) {
/* TODO: XXX: this situation should be handled earlier:
* The destination is neither ALL_PIM_ROUTERS neither me
*/
return FALSE;
}
/* Probably unicasted from the current DR */
if (cand_rp_list) {
/* Hmmm, I do have a Cand-RP-list, but some neighbor has a
* different opinion and is unicasting it to me. Ignore this guy.
*/
return FALSE;
}
for (vifi = 0; vifi < numvifs; vifi++) {
if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER))
continue;
if (uvifs[vifi].uv_lcl_addr == dst) {
incoming = vifi;
break;
}
}
if (incoming == NO_VIF) {
/* Cannot find the receiving iif toward that DR */
IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP)
logit(LOG_DEBUG, 0, "Unicast boostrap message from %s to ignored: cannot find iif",
inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
return FALSE;
}
/* TODO: check the sender is directly connected and I am really the DR */
}
if (cand_rp_flag == TRUE) {
/* If change in the BSR address, schedule immediate Cand-RP-Adv */
/* TODO: use some random delay? */
if (new_bsr_address != curr_bsr_address)
SET_TIMER(pim_cand_rp_adv_timer, 0);
}
/* Forward the BSR Message first and then update the RP-set list */
/* TODO: if the message was unicasted to me, resend? */
for (vifi = 0; vifi < numvifs; vifi++) {
if (vifi == incoming)
continue;
if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_NONBRS))
continue;
memcpy(pim_send_buf + sizeof(struct ip), msg, len);
send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
PIM_BOOTSTRAP, len - sizeof(pim_header_t));
}
max_data = (uint8_t *)msg + len;
/* TODO: XXX: this 22 is HARDCODING!!! Do a bunch of definitions
* and make it stylish!
*/
min_datalen = 22;
if (new_bsr_fragment_tag != curr_bsr_fragment_tag || new_bsr_address != curr_bsr_address) {
/* Throw away the old segment */
delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list);
}
curr_bsr_address = new_bsr_address;
curr_bsr_priority = new_bsr_priority;
curr_bsr_fragment_tag = new_bsr_fragment_tag;
MASKLEN_TO_MASK(new_bsr_hash_masklen, curr_bsr_hash_mask);
SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
while (data + min_datalen <= max_data) {
GET_EGADDR(&curr_group_addr, data);
GET_BYTE(curr_rp_count, data);
GET_BYTE(curr_frag_rp_count, data);
GET_HOSTSHORT(reserved_short, data);
MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
if (curr_rp_count == 0) {
delete_grp_mask(&cand_rp_list, &grp_mask_list,
curr_group_addr.mcast_addr, curr_group_mask);
continue;
}
if (curr_rp_count == curr_frag_rp_count) {
/* Add all RPs */
while (curr_frag_rp_count--) {
GET_EUADDR(&curr_rp_addr, data);
GET_HOSTSHORT(curr_rp_holdtime, data);
GET_BYTE(curr_rp_priority, data);
GET_BYTE(reserved_byte, data);
MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
curr_rp_addr.unicast_addr, curr_rp_priority,
curr_rp_holdtime, curr_group_addr.mcast_addr,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
continue;
}
/*
* This is a partial list of the RPs for this group prefix.
* Save until all segments arrive.
*/
prefix_h = ntohl(curr_group_addr.mcast_addr & curr_group_mask);
for (grp_mask = segmented_grp_mask_list; grp_mask; grp_mask = grp_mask->next) {
if (ntohl(grp_mask->group_addr & grp_mask->group_mask) > prefix_h)
continue;
break;
}
if (grp_mask
&& (grp_mask->group_addr == curr_group_addr.mcast_addr)
&& (grp_mask->group_mask == curr_group_mask)
&& (grp_mask->group_rp_number + curr_frag_rp_count == curr_rp_count)) {
/* All missing PRs have arrived. Add all RP entries */
while (curr_frag_rp_count--) {
GET_EUADDR(&curr_rp_addr, data);
GET_HOSTSHORT(curr_rp_holdtime, data);
GET_BYTE(curr_rp_priority, data);
GET_BYTE(reserved_byte, data);
MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
add_rp_grp_entry(&cand_rp_list,
&grp_mask_list,
curr_rp_addr.unicast_addr,
curr_rp_priority,
curr_rp_holdtime,
curr_group_addr.mcast_addr,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
/* Add the rest from the previously saved segments */
for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp->grp_rp_next) {
add_rp_grp_entry(&cand_rp_list,
&grp_mask_list,
grp_rp->rp->rpentry->address,
grp_rp->priority,
grp_rp->holdtime,
curr_group_addr.mcast_addr,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
delete_grp_mask(&segmented_cand_rp_list,
&segmented_grp_mask_list,
curr_group_addr.mcast_addr,
curr_group_mask);
} else {
/* Add the partially received RP-list to the group of pending RPs*/
while (curr_frag_rp_count--) {
GET_EUADDR(&curr_rp_addr, data);
GET_HOSTSHORT(curr_rp_holdtime, data);
GET_BYTE(curr_rp_priority, data);
GET_BYTE(reserved_byte, data);
MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask);
add_rp_grp_entry(&segmented_cand_rp_list,
&segmented_grp_mask_list,
curr_rp_addr.unicast_addr,
curr_rp_priority,
curr_rp_holdtime,
curr_group_addr.mcast_addr,
curr_group_mask,
curr_bsr_hash_mask,
curr_bsr_fragment_tag);
}
}
}
/* Garbage collection. Check all group prefixes and if the
* fragment_tag for a group-prefix is the same as curr_bsr_fragment_tag,
* then remove all RPs for this group-prefix which have different
* fragment tag.
*/
for (grp_mask = grp_mask_list; grp_mask; grp_mask = grp_mask_next) {
grp_mask_next = grp_mask->next;
if (grp_mask->fragment_tag == curr_bsr_fragment_tag) {
for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp_next) {
grp_rp_next = grp_rp->grp_rp_next;
if (grp_rp->fragment_tag != curr_bsr_fragment_tag)
delete_rp_grp_entry(&cand_rp_list, &grp_mask_list, grp_rp);
}
}
}
/* Cleanup also the list used by incompleted segments */
for (grp_mask = segmented_grp_mask_list; grp_mask; grp_mask = grp_mask_next) {
grp_mask_next = grp_mask->next;
if (grp_mask->fragment_tag == curr_bsr_fragment_tag) {
for (grp_rp = grp_mask->grp_rp_next; grp_rp; grp_rp = grp_rp_next) {
grp_rp_next = grp_rp->grp_rp_next;
if (grp_rp->fragment_tag != curr_bsr_fragment_tag)
delete_rp_grp_entry(&segmented_cand_rp_list, &segmented_grp_mask_list, grp_rp);
}
}
}
return TRUE;
}
void send_pim_bootstrap(void)
{
size_t len;
vifi_t vifi;
if ((len = create_pim_bootstrap_message(pim_send_buf))) {
for (vifi = 0; vifi < numvifs; vifi++) {
if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER))
continue;
send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr,
allpimrouters_group, PIM_BOOTSTRAP, len);
}
}
}
/************************************************************************
* PIM_CAND_RP_ADV
************************************************************************/
/*
* If I am the Bootstrap router, process the advertisement, otherwise
* ignore it.
*/
#define PIM_CAND_RP_ADV_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN)
int receive_pim_cand_rp_adv(uint32_t src, uint32_t dst __attribute__((unused)), char *msg, size_t len)
{
uint8_t prefix_cnt;
uint8_t priority;
uint16_t holdtime;
pim_encod_uni_addr_t euaddr;
pim_encod_grp_addr_t egaddr;
uint8_t *data_ptr;
uint32_t grp_mask;
/* Checksum */
if (inet_cksum((uint16_t *)msg, len))
return FALSE;
/* if I am not the bootstrap RP, then do not accept the message */
if (cand_bsr_flag == FALSE || curr_bsr_address != my_bsr_address)
return FALSE;
/* sanity check for the minimum length */
if (len < PIM_CAND_RP_ADV_MINLEN) {
logit(LOG_NOTICE, 0, "%s(): cand_RP message size(%u) is too short from %s",
__func__, len, inet_fmt(src, s1, sizeof(s1)));
return FALSE;
}
data_ptr = (uint8_t *)(msg + sizeof(pim_header_t));
/* Parse the CAND_RP_ADV message */
/* TODO: XXX: check len whether it is at least the minimum */
GET_BYTE(prefix_cnt, data_ptr);
GET_BYTE(priority, data_ptr);
GET_HOSTSHORT(holdtime, data_ptr);
GET_EUADDR(&euaddr, data_ptr);
if (prefix_cnt == 0) {
/* The default 224.0.0.0 and masklen of 4 */
MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LEN, grp_mask);
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
euaddr.unicast_addr, priority, holdtime,
htonl(ALL_MCAST_GROUPS_ADDR), grp_mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
return TRUE;
}
while (prefix_cnt--) {
GET_EGADDR(&egaddr, data_ptr);
MASKLEN_TO_MASK(egaddr.masklen, grp_mask);
/* Do not advertise internal virtual RP for SSM groups */
if (!IN_PIM_SSM_RANGE(egaddr.mcast_addr)) {
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
euaddr.unicast_addr, priority, holdtime,
egaddr.mcast_addr, grp_mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
}
/* TODO: Check for len */
}
return TRUE;
}
int send_pim_cand_rp_adv(void)
{
uint8_t prefix_cnt;
uint32_t mask;
pim_encod_grp_addr_t addr;
uint8_t *data;
if (!inet_valid_host(curr_bsr_address))
return FALSE; /* No BSR yet */
if (curr_bsr_address == my_bsr_address) {
/* I am the BSR and have to include my own group-prefix stuff */
prefix_cnt = *cand_rp_adv_message.prefix_cnt_ptr;
if (prefix_cnt == 0) {
/* The default 224.0.0.0 and masklen of 4 */
MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LEN, mask);
add_rp_grp_entry(&cand_rp_list, &grp_mask_list,
my_cand_rp_address, my_cand_rp_priority,
my_cand_rp_holdtime,
htonl(ALL_MCAST_GROUPS_ADDR), mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
return TRUE;
}
/* TODO: hardcoding!! */
data = cand_rp_adv_message.buffer + (4 + 6);
while (prefix_cnt--) {
GET_EGADDR(&addr, data);
MASKLEN_TO_MASK(addr.masklen, mask);
add_rp_grp_entry(&cand_rp_list,
&grp_mask_list,
my_cand_rp_address, my_cand_rp_priority,
my_cand_rp_holdtime,
addr.mcast_addr, mask,
my_bsr_hash_mask,
curr_bsr_fragment_tag);
/* TODO: Check for len */
}
return TRUE;
}
data = (uint8_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t));
memcpy(data, cand_rp_adv_message.buffer, cand_rp_adv_message.message_size);
send_pim_unicast(pim_send_buf, 0, my_cand_rp_address, curr_bsr_address,
PIM_CAND_RP_ADV, cand_rp_adv_message.message_size);
return TRUE;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "ellemtel"
* c-basic-offset: 4
* End:
*/
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/macroqz/pimd.git
git@gitee.com:macroqz/pimd.git
macroqz
pimd
pimd
master

搜索帮助

23e8dbc6 1850385 7e0993f3 1850385