Ignore:
Timestamp:
Feb 6, 2007, 3:24:55 AM (15 years ago)
Author:
joshe
Message:

Clean up NAT-PMP code a little.
Correctly handle a NAT-PMP device mapping a different public port than requested.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/natpmp.c

    r1443 r1460  
    3434#define PMP_OPCODE_ADDTCP       2
    3535#define PMP_LIFETIME            3600    /* secs, one hour */
    36 #define PMP_RESPCODE_OK         0
    37 #define PMP_RESPCODE_BADVERS    1
    38 #define PMP_RESPCODE_REFUSED    2
    39 #define PMP_RESPCODE_NETDOWN    3
    40 #define PMP_RESPCODE_NOMEM      4
    41 #define PMP_RESPCODE_BADOPCODE  5
     36#define PMP_RESULT_OK           0
     37#define PMP_RESULT_BADVERS      1
     38#define PMP_RESULT_REFUSED      2
     39#define PMP_RESULT_NETDOWN      3
     40#define PMP_RESULT_NOMEM        4
     41#define PMP_RESULT_BADOPCODE    5
    4242
    4343#define PMP_OPCODE_FROM_RESPONSE( op )  ( 0x80 ^ (op) )
     
    6464    uint64_t             retry;
    6565    uint64_t             timeout;
    66     int                  port;
    67     tr_natpmp_uptime_t * uptime;
     66    int                  askport;
     67    int                  gotport;
    6868} tr_natpmp_req_t;
    6969
     
    9090};
    9191
     92typedef struct tr_natpmp_parse_s
     93{
     94    unsigned int tmpfail : 1;
     95    uint32_t     seconds;
     96    uint16_t     port;
     97    uint32_t     lifetime;
     98}
     99tr_natpmp_parse_t;
     100
    92101static int
    93102checktime( tr_natpmp_uptime_t * uptime, uint32_t seen );
     
    95104killsock( int * fd );
    96105static tr_natpmp_req_t *
    97 newreq( int adding, struct in_addr addr, int port,
    98         tr_natpmp_uptime_t * uptime );
     106newreq( int adding, struct in_addr addr, int port );
     107static void
     108killreq( tr_natpmp_req_t ** req );
     109static void
     110resetreq( tr_natpmp_req_t * req );
    99111static tr_tristate_t
    100 pulsereq( tr_natpmp_req_t * req, uint64_t * renew );
     112pulsereq( tr_natpmp_t * req );
     113static int
     114sendreq( tr_natpmp_req_t * req );
    101115static int
    102116mcastsetup();
    103117static void
    104118mcastpulse( tr_natpmp_t * pmp );
    105 static void
    106 killreq( tr_natpmp_req_t ** req );
    107 static int
    108 sendrequest( int adding, int fd, int port );
    109119static tr_tristate_t
    110 readrequest( uint8_t * buf, int len, int adding, int port,
    111              tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail );
     120parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse );
    112121
    113122tr_natpmp_t *
     
    159168            pmp->mcastfd = mcastsetup();
    160169        }
    161         /* XXX should I change state? */
    162170    }
    163171
     
    184192                if( NULL != pmp->req )
    185193                {
    186                     pmp->mappedport = pmp->req->port;
     194                    pmp->mappedport = pmp->req->gotport;
    187195                    killreq( &pmp->req );
    188196                    pmp->state = PMP_STATE_DELETING;
     
    271279    /* try to send at least one delete request if we have a port mapping */
    272280    tr_natpmpStop( pmp );
    273     tr_natpmpPulse( pmp );
     281    tr_natpmpPulse( pmp, NULL );
    274282
    275283    tr_lockLock( &pmp->lock );
     
    280288
    281289void
    282 tr_natpmpPulse( tr_natpmp_t * pmp )
     290tr_natpmpPulse( tr_natpmp_t * pmp, int * publicPort )
    283291{
    284292    tr_lockLock( &pmp->lock );
     
    319327                    else
    320328                    {
    321                         pmp->req = newreq( 1, pmp->dest, pmp->newport,
    322                                            &pmp->uptime );
     329                        pmp->req = newreq( 1, pmp->dest, pmp->newport );
    323330                        if( NULL == pmp->req )
    324331                        {
     
    330337                if( PMP_STATE_ADDING == pmp->state )
    331338                {
    332                     switch( pulsereq( pmp->req, &pmp->renew ) )
     339                    switch( pulsereq( pmp ) )
    333340                    {
    334341                        case TR_NET_ERROR:
     
    342349                                pmp->state = PMP_STATE_TMPFAIL;
    343350                                tr_dbg( "nat-pmp state add -> err on pulse" );
    344                                 if( pmp->req->port == pmp->newport )
     351                                if( pmp->req->askport == pmp->newport )
    345352                                {
    346353                                    pmp->newport = 0;
     
    355362                            break;
    356363                        case TR_NET_OK:
    357                             pmp->mappedport = pmp->req->port;
     364                            pmp->mappedport = pmp->req->gotport;
     365                            if( pmp->mappedport != pmp->newport &&
     366                                pmp->newport == pmp->req->askport )
     367                            {
     368                                pmp->newport = pmp->req->gotport;
     369                            }
    358370                            killreq( &pmp->req );
    359371                            pmp->state = PMP_STATE_MAPPED;
     
    373385                {
    374386                    assert( 0 < pmp->mappedport );
    375                     pmp->req = newreq( 0, pmp->dest, pmp->mappedport,
    376                                        &pmp->uptime );
     387                    pmp->req = newreq( 0, pmp->dest, pmp->mappedport );
    377388                    if( NULL == pmp->req )
    378389                    {
     
    383394                if( PMP_STATE_DELETING == pmp->state )
    384395                {
    385                     switch( pulsereq( pmp->req, &pmp->renew ) )
     396                    switch( pulsereq( pmp ) )
    386397                    {
    387398                        case TR_NET_ERROR:
     
    408419                        case TR_NET_OK:
    409420                            tr_dbg( "nat-pmp state del -> idle with port %i",
    410                                     pmp->req->port);
    411                             tr_inf( "nat-pmp unmapped port %i", pmp->req->port );
     421                                    pmp->req->askport);
     422                            tr_inf( "nat-pmp unmapped port %i",
     423                                    pmp->req->askport );
    412424                            pmp->mapped = 0;
    413425                            pmp->mappedport = -1;
     
    445457    }
    446458
     459    if( NULL != publicPort )
     460    {
     461        *publicPort = -1;
     462    }
     463
    447464    tr_lockUnlock( &pmp->lock );
    448465}
     
    483500
    484501static tr_natpmp_req_t *
    485 newreq( int adding, struct in_addr addr, int port,
    486         tr_natpmp_uptime_t * uptime )
     502newreq( int adding, struct in_addr addr, int port )
    487503{
    488504    tr_natpmp_req_t * ret;
    489     uint64_t          now;
    490505
    491506    ret = calloc( 1, sizeof( *ret ) );
    492507    if( NULL == ret )
    493508    {
    494         goto err;
    495     }
    496     ret->fd = -1;
     509        return NULL;
     510    }
     511
    497512    ret->fd = tr_netOpenUDP( addr, htons( PMP_PORT ), 1 );
    498513    if( 0 > ret->fd )
    499514    {
    500         goto err;
    501     }
    502     if( sendrequest( adding, ret->fd, port ) )
    503     {
    504         goto err;
    505     }
     515        free( ret );
     516        return NULL;
     517    }
     518
     519    ret->adding  = adding;
     520    ret->askport = port;
     521    ret->gotport = port;
     522    resetreq( ret );
     523    if( sendreq( ret ) )
     524    {
     525        killreq( &ret );
     526        return NULL;
     527    }
     528
     529    return ret;
     530}
     531
     532static void
     533killreq( tr_natpmp_req_t ** req )
     534{
     535    if( NULL != *req )
     536    {
     537        killsock( &(*req)->fd );
     538        free( *req );
     539        *req = NULL;
     540    }
     541}
     542
     543static void
     544resetreq( tr_natpmp_req_t * req )
     545{
     546    uint64_t now;
    506547
    507548    now          = tr_date();
    508     ret->adding  = adding;
    509     ret->delay   = PMP_INITIAL_DELAY;
    510     ret->retry   = now + PMP_INITIAL_DELAY;
    511     ret->timeout = now + PMP_TOTAL_DELAY;
    512     ret->port    = port;
    513     ret->uptime  = uptime;
    514 
    515     return ret;
    516 
    517   err:
    518     if( NULL != ret )
    519     {
    520         killsock( &ret->fd );
    521     }
    522     free( ret );
    523 
    524     return NULL;
     549    req->delay   = PMP_INITIAL_DELAY;
     550    req->retry   = now;
     551    req->timeout = now + PMP_TOTAL_DELAY;
    525552}
    526553
    527554static tr_tristate_t
    528 pulsereq( tr_natpmp_req_t * req, uint64_t * renew )
    529 {
     555pulsereq( tr_natpmp_t * pmp )
     556{
     557    tr_natpmp_req_t  * req = pmp->req;
    530558    struct sockaddr_in sin;
    531559    uint8_t            buf[16];
    532     int                res, tmpfail;
     560    int                res;
    533561    uint64_t           now;
    534562    tr_tristate_t      ret;
     563    tr_natpmp_parse_t  parse;
    535564
    536565    now = tr_date();
    537 
     566    /* check for timeout */
    538567    if( now >= req->timeout )
    539568    {
     
    542571        return TR_NET_ERROR;
    543572    }
    544 
    545     if( now >= req->retry )
    546     {
    547         if( sendrequest( req->adding, req->fd, req->port ) )
    548         {
    549             return TR_NET_ERROR;
    550         }
    551         req->delay *= 2;
    552         req->timeout = now + req->delay;
    553     }
    554 
     573    /* send another request  if it's been long enough */
     574    if( now >= req->retry && sendreq( req ) )
     575    {
     576        return TR_NET_ERROR;
     577    }
     578
     579    /* check for incoming packets */
    555580    res = tr_netRecvFrom( req->fd, buf, sizeof( buf ), &sin );
    556581    if( TR_NET_BLOCK & res )
     
    572597    }
    573598
     599    /* parse the packet */
    574600    tr_dbg( "nat-pmp read %i byte response", res );
    575 
    576     ret = readrequest( buf, res, req->adding, req->port, req->uptime, renew,
    577                        &tmpfail );
    578     req->tmpfail = ( tmpfail ? 1 : 0 );
     601    ret = parseresponse( buf, res, req->askport, &parse );
     602    req->tmpfail = parse.tmpfail;
     603    /* check for device reset */
     604    if( checktime( &pmp->uptime, parse.seconds ) )
     605    {
     606        pmp->renew = 0;
     607        tr_inf( "detected nat-pmp device reset" );
     608        resetreq( req );
     609        ret = TR_NET_WAIT;
     610    }
     611    if( TR_NET_OK == ret && req->adding )
     612    {
     613        if( req->askport != parse.port )
     614        {
     615            tr_dbg( "nat-pmp received %i for public port instead of %i",
     616                    parse.port, req->askport );
     617            req->gotport = parse.port;
     618        }
     619        tr_dbg( "nat-pmp set renew to half of %u", parse.lifetime );
     620        pmp->renew = now + ( parse.lifetime / 2 * 1000 );
     621    }
     622
    579623    return ret;
     624}
     625
     626static int
     627sendreq( tr_natpmp_req_t * req )
     628{
     629    uint8_t buf[12];
     630    int res;
     631
     632    bzero( buf, sizeof( buf ) );
     633    buf[0] = PMP_VERSION;
     634    buf[1] = PMP_OPCODE_ADDTCP;
     635    PMP_TOBUF16( buf + 4, req->askport );
     636    if( req->adding )
     637    {
     638        PMP_TOBUF16( buf + 6, req->askport );
     639        PMP_TOBUF32( buf + 8, PMP_LIFETIME );
     640    }
     641
     642    res = tr_netSend( req->fd, buf, sizeof( buf ) );
     643    if( TR_NET_CLOSE & res && EHOSTUNREACH == errno )
     644    {
     645        res = TR_NET_BLOCK;
     646    }
     647    if( TR_NET_CLOSE & res )
     648    {
     649        tr_err( "failed to send nat-pmp request (%s)", strerror( errno ) );
     650        return 1;
     651    }
     652    else if( !( TR_NET_BLOCK & res ) )
     653    {
     654        /* XXX is it all right to assume the entire thing is written? */
     655        req->retry  = tr_date() + req->delay;
     656        req->delay *= 2;
     657    }
     658    return 0;
    580659}
    581660
     
    605684    int                res;
    606685    char               dbgstr[INET_ADDRSTRLEN];
     686    tr_natpmp_parse_t  parse;
    607687
    608688    res = tr_netRecvFrom( pmp->mcastfd, buf, sizeof( buf ), &sin );
     
    628708    }
    629709
    630     if( TR_NET_OK == readrequest( buf, res, 0, -1, &pmp->uptime, &pmp->renew, NULL ) &&
    631         PMP_STATE_NOBODYHOME == pmp->state )
    632     {
    633         tr_dbg( "nat-pmp state notfound -> idle" );
    634         pmp->state = PMP_STATE_IDLE;
    635     }
    636 }
    637 
    638 static void
    639 killreq( tr_natpmp_req_t ** req )
    640 {
    641     if( NULL != *req )
    642     {
    643         killsock( &(*req)->fd );
    644         free( *req );
    645         *req = NULL;
    646     }
    647 }
    648 
    649 static int
    650 sendrequest( int adding, int fd, int port )
    651 {
    652     uint8_t buf[12];
    653     int res;
    654 
    655     buf[0] = PMP_VERSION;
    656     buf[1] = PMP_OPCODE_ADDTCP;
    657     buf[2] = 0;
    658     buf[3] = 0;
    659     PMP_TOBUF16( buf + 4, port );
    660     if( adding )
    661     {
    662         PMP_TOBUF16( buf + 6, port );
    663         PMP_TOBUF32( buf + 8, PMP_LIFETIME );
    664     }
    665     else
    666     {
    667         PMP_TOBUF16( buf + 6, 0 );
    668         PMP_TOBUF32( buf + 8, 0 );
    669     }
    670 
    671     res = tr_netSend( fd, buf, sizeof( buf ) );
    672     if( TR_NET_CLOSE & res && EHOSTUNREACH == errno )
    673     {
    674         res = TR_NET_BLOCK;
    675     }
    676     /* XXX is it all right to assume the entire thing is written? */
    677 
    678     /* XXX I should handle blocking here */
    679 
    680     return ( ( TR_NET_CLOSE | TR_NET_BLOCK ) & res  ? 1 : 0 );
     710    if( TR_NET_OK == parseresponse( buf, res, -1, &parse ) )
     711    {
     712        if( checktime( &pmp->uptime, parse.seconds ) )
     713        {
     714            pmp->renew = 0;
     715            tr_inf( "detected nat-pmp device reset" );
     716            if( NULL != pmp->req )
     717            {
     718                resetreq( pmp->req );
     719            }
     720        }
     721        if( PMP_STATE_NOBODYHOME == pmp->state )
     722        {
     723            tr_dbg( "nat-pmp state notfound -> idle" );
     724            pmp->state = PMP_STATE_IDLE;
     725        }
     726    }
    681727}
    682728
    683729static tr_tristate_t
    684 readrequest( uint8_t * buf, int len, int adding, int port,
    685              tr_natpmp_uptime_t * uptime, uint64_t * renew, int * tmpfail )
    686 {
    687     uint8_t            version, opcode, wantedopcode;
    688     uint16_t           rescode, privport, pubport;
    689     uint32_t           seconds, lifetime;
    690 
    691     assert( !adding || NULL != tmpfail );
    692     if( NULL != tmpfail )
    693     {
    694         *tmpfail = 0;
    695     }
    696     if( 4 > len )
     730parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse )
     731{
     732    int version, respopcode, opcode, wantedopcode, rescode, privport;
     733
     734    bzero( parse, sizeof( *parse ) );
     735
     736    if( 8 > len )
    697737    {
    698738        tr_err( "read truncated %i byte nat-pmp response packet", len );
    699739        return TR_NET_ERROR;
    700740    }
     741
     742    /* parse the first 8 bytes: version, opcode, and result code */
    701743    version      = buf[0];
    702     opcode       = buf[1];
     744    respopcode   = buf[1];
     745    opcode       = PMP_OPCODE_FROM_RESPONSE( respopcode );
     746    wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP );
    703747    rescode      = PMP_FROMBUF16( buf + 2 );
    704     wantedopcode = ( 0 < port ? PMP_OPCODE_ADDTCP : PMP_OPCODE_GETIP );
    705 
    706     if( !PMP_OPCODE_IS_RESPONSE( opcode ) )
     748
     749    if( PMP_VERSION != version )
     750    {
     751        tr_err( "unknown nat-pmp version %hhu", buf[0] );
     752        return TR_NET_ERROR;
     753    }
     754    if( !PMP_OPCODE_IS_RESPONSE( respopcode ) )
    707755    {
    708756        tr_dbg( "nat-pmp ignoring request packet" );
    709757        return TR_NET_WAIT;
    710758    }
    711     opcode = PMP_OPCODE_FROM_RESPONSE( opcode );
    712 
    713     if( PMP_VERSION != version )
    714     {
    715         tr_err( "bad nat-pmp version %hhu", buf[0] );
     759    if( wantedopcode != opcode )
     760    {
     761        tr_err( "unknown nat-pmp opcode %hhu", opcode );
    716762        return TR_NET_ERROR;
    717763    }
    718     if( wantedopcode != opcode )
    719     {
    720         tr_err( "bad nat-pmp opcode %hhu", opcode );
    721         return TR_NET_ERROR;
    722     }
     764
    723765    switch( rescode )
    724766    {
    725         case PMP_RESPCODE_OK:
     767        case PMP_RESULT_OK:
    726768            break;
    727         case PMP_RESPCODE_REFUSED:
    728         case PMP_RESPCODE_NETDOWN:
    729         case PMP_RESPCODE_NOMEM:
    730             if( NULL != tmpfail )
    731             {
    732                 *tmpfail = 1;
    733             }
    734             /* fallthrough */
     769        case PMP_RESULT_REFUSED:
     770            tr_err( "nat-pmp mapping failed: refused/unauthorized/disabled" );
     771            parse->tmpfail = 1;
     772            return TR_NET_ERROR;
     773        case PMP_RESULT_NETDOWN:
     774            tr_err( "nat-pmp mapping failed: network down" );
     775            parse->tmpfail = 1;
     776            return TR_NET_ERROR;
     777        case PMP_RESULT_NOMEM:
     778            tr_err( "nat-pmp mapping refused: insufficient resources" );
     779            parse->tmpfail = 1;
     780            return TR_NET_ERROR;
    735781        default:
    736             tr_err( "bad nat-pmp result code %hu", rescode );
     782            tr_err( "nat-pmp mapping refused: unknown result code: %hu",
     783                    rescode );
    737784            return TR_NET_ERROR;
    738785    }
    739786
    740     if( 8 > len )
    741     {
    742         tr_err( "read truncated %i byte nat-pmp response packet", len );
    743         return TR_NET_ERROR;
    744     }
    745     seconds = PMP_FROMBUF32( buf + 4 );
    746 
    747     if( checktime( uptime, seconds ) )
    748     {
    749         *renew = 0;
    750         tr_inf( "detected nat-pmp device reset" );
    751         /* XXX should reset retry counter here */
    752         return TR_NET_WAIT;
    753     }
    754 
    755     if( 0 <= port )
    756     {
    757         assert( PMP_OPCODE_ADDTCP == wantedopcode );
     787    parse->seconds = PMP_FROMBUF32( buf + 4 );
     788    if( PMP_OPCODE_ADDTCP == opcode )
     789    {
    758790        if( 16 > len )
    759791        {
     
    761793            return TR_NET_ERROR;
    762794        }
    763         privport = PMP_FROMBUF16( buf + 8 );
    764         pubport  = PMP_FROMBUF16( buf + 10 );
    765         lifetime = PMP_FROMBUF32( buf + 12 );
     795        privport        = PMP_FROMBUF16( buf + 8 );
     796        parse->port     = PMP_FROMBUF16( buf + 10 );
     797        parse->lifetime = PMP_FROMBUF32( buf + 12 );
    766798
    767799        if( port != privport )
    768800        {
    769             /* private port doesn't match, ignore it */
    770801            tr_dbg( "nat-pmp ignoring message for port %i, expected port %i",
    771802                    privport, port );
    772803            return TR_NET_WAIT;
    773804        }
    774 
    775         if( adding )
    776         {
    777             if( port != pubport )
    778             {
    779                 *tmpfail = 1;
    780                 /* XXX should just start announcing the pub port we're given */
    781                 return TR_NET_ERROR;
    782             }
    783             tr_dbg( "nat-pmp set renew to half of %u", lifetime );
    784             *renew = tr_date() + ( lifetime / 2 * 1000 );
    785         }
    786805    }
    787806
Note: See TracChangeset for help on using the changeset viewer.