summaryrefslogtreecommitdiffstats
path: root/contrib/tcpdump/print-icmp6.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tcpdump/print-icmp6.c')
-rw-r--r--contrib/tcpdump/print-icmp6.c259
1 files changed, 183 insertions, 76 deletions
diff --git a/contrib/tcpdump/print-icmp6.c b/contrib/tcpdump/print-icmp6.c
index 40d71df..88580b2 100644
--- a/contrib/tcpdump/print-icmp6.c
+++ b/contrib/tcpdump/print-icmp6.c
@@ -20,8 +20,8 @@
*/
#ifndef lint
-static const char rcsid[] =
- "@(#) $Header: /tcpdump/master/tcpdump/print-icmp6.c,v 1.56.4.1 2002/05/31 10:08:31 guy Exp $";
+static const char rcsid[] _U_ =
+ "@(#) $Header: /tcpdump/master/tcpdump/print-icmp6.c,v 1.72.2.4 2004/03/24 00:14:09 guy Exp $";
#endif
#ifdef HAVE_CONFIG_H
@@ -30,27 +30,18 @@ static const char rcsid[] =
#ifdef INET6
-#include <ctype.h>
-
-#include <sys/param.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-
-#include <netinet/in.h>
-
-#include <arpa/inet.h>
+#include <tcpdump-stdinc.h>
#include <stdio.h>
#include <string.h>
-#include <netdb.h>
#include "ip6.h"
#include "icmp6.h"
+#include "ipproto.h"
#include "interface.h"
#include "addrtoname.h"
+#include "extract.h"
#include "udp.h"
#include "ah.h"
@@ -58,12 +49,12 @@ static const char rcsid[] =
static const char *get_rtpref(u_int);
static const char *get_lifetime(u_int32_t);
static void print_lladdr(const u_char *, size_t);
-void icmp6_opt_print(const u_char *, int);
-void mld6_print(const u_char *);
-static struct udphdr *get_upperlayer(u_char *, int *);
+static void icmp6_opt_print(const u_char *, int);
+static void mld6_print(const u_char *);
+static struct udphdr *get_upperlayer(u_char *, u_int *);
static void dnsname_print(const u_char *, const u_char *);
-void icmp6_nodeinfo_print(int, const u_char *, const u_char *);
-void icmp6_rrenum_print(int, const u_char *, const u_char *);
+static void icmp6_nodeinfo_print(u_int, const u_char *, const u_char *);
+static void icmp6_rrenum_print(const u_char *, const u_char *);
#ifndef abs
#define abs(a) ((0 < (a)) ? (a) : -(a))
@@ -110,8 +101,51 @@ print_lladdr(const u_int8_t *p, size_t l)
}
}
+static int icmp6_cksum(const struct ip6_hdr *ip6, const struct icmp6_hdr *icp,
+ u_int len)
+{
+ size_t i;
+ register const u_int16_t *sp;
+ u_int32_t sum;
+ union {
+ struct {
+ struct in6_addr ph_src;
+ struct in6_addr ph_dst;
+ u_int32_t ph_len;
+ u_int8_t ph_zero[3];
+ u_int8_t ph_nxt;
+ } ph;
+ u_int16_t pa[20];
+ } phu;
+
+ /* pseudo-header */
+ memset(&phu, 0, sizeof(phu));
+ phu.ph.ph_src = ip6->ip6_src;
+ phu.ph.ph_dst = ip6->ip6_dst;
+ phu.ph.ph_len = htonl(len);
+ phu.ph.ph_nxt = IPPROTO_ICMPV6;
+
+ sum = 0;
+ for (i = 0; i < sizeof(phu.pa) / sizeof(phu.pa[0]); i++)
+ sum += phu.pa[i];
+
+ sp = (const u_int16_t *)icp;
+
+ for (i = 0; i < (len & ~1); i += 2)
+ sum += *sp++;
+
+ if (len & 1)
+ sum += htons((*(const u_int8_t *)sp) << 8);
+
+ while (sum > 0xffff)
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = ~sum & 0xffff;
+
+ return (sum);
+}
+
void
-icmp6_print(const u_char *bp, const u_char *bp2)
+icmp6_print(const u_char *bp, u_int length, const u_char *bp2, int fragmented)
{
const struct icmp6_hdr *dp;
const struct ip6_hdr *ip;
@@ -121,7 +155,7 @@ icmp6_print(const u_char *bp, const u_char *bp2)
int dport;
const u_char *ep;
char buf[256];
- int icmp6len, prot;
+ u_int prot;
dp = (struct icmp6_hdr *)bp;
ip = (struct ip6_hdr *)bp2;
@@ -129,13 +163,21 @@ icmp6_print(const u_char *bp, const u_char *bp2)
str = buf;
/* 'ep' points to the end of available data. */
ep = snapend;
- if (ip->ip6_plen)
- icmp6len = (ntohs(ip->ip6_plen) + sizeof(struct ip6_hdr) -
- (bp - bp2));
- else /* XXX: jumbo payload case... */
- icmp6len = snapend - bp;
- TCHECK(dp->icmp6_code);
+ TCHECK(dp->icmp6_cksum);
+
+ if (vflag && !fragmented) {
+ int sum = dp->icmp6_cksum;
+
+ if (TTEST2(bp[0], length)) {
+ sum = icmp6_cksum(ip, dp, length);
+ if (sum != 0)
+ (void)printf("[bad icmp6 cksum %x!] ", sum);
+ else
+ (void)printf("[icmp6 sum ok] ");
+ }
+ }
+
switch (dp->icmp6_type) {
case ICMP6_DST_UNREACH:
TCHECK(oip->ip6_dst);
@@ -162,7 +204,7 @@ icmp6_print(const u_char *bp, const u_char *bp2)
== NULL)
goto trunc;
- dport = ntohs(ouh->uh_dport);
+ dport = EXTRACT_16BITS(&ouh->uh_dport);
switch (prot) {
case IPPROTO_TCP:
printf("icmp6: %s tcp port %s unreachable",
@@ -190,7 +232,7 @@ icmp6_print(const u_char *bp, const u_char *bp2)
break;
case ICMP6_PACKET_TOO_BIG:
TCHECK(dp->icmp6_mtu);
- printf("icmp6: too big %u", (u_int32_t)ntohl(dp->icmp6_mtu));
+ printf("icmp6: too big %u", EXTRACT_32BITS(&dp->icmp6_mtu));
break;
case ICMP6_TIME_EXCEEDED:
TCHECK(oip->ip6_dst);
@@ -213,15 +255,15 @@ icmp6_print(const u_char *bp, const u_char *bp2)
switch (dp->icmp6_code) {
case ICMP6_PARAMPROB_HEADER:
printf("icmp6: parameter problem errorneous - octet %u",
- (u_int32_t)ntohl(dp->icmp6_pptr));
+ EXTRACT_32BITS(&dp->icmp6_pptr));
break;
case ICMP6_PARAMPROB_NEXTHEADER:
printf("icmp6: parameter problem next header - octet %u",
- (u_int32_t)ntohl(dp->icmp6_pptr));
+ EXTRACT_32BITS(&dp->icmp6_pptr));
break;
case ICMP6_PARAMPROB_OPTION:
printf("icmp6: parameter problem option - octet %u",
- (u_int32_t)ntohl(dp->icmp6_pptr));
+ EXTRACT_32BITS(&dp->icmp6_pptr));
break;
default:
printf("icmp6: parameter problem code-#%d",
@@ -230,10 +272,12 @@ icmp6_print(const u_char *bp, const u_char *bp2)
}
break;
case ICMP6_ECHO_REQUEST:
- printf("icmp6: echo request");
- break;
case ICMP6_ECHO_REPLY:
- printf("icmp6: echo reply");
+ TCHECK(dp->icmp6_seq);
+ printf("icmp6: echo %s seq %u",
+ dp->icmp6_type == ICMP6_ECHO_REQUEST ?
+ "request" : "reply",
+ EXTRACT_16BITS(&dp->icmp6_seq));
break;
case ICMP6_MEMBERSHIP_QUERY:
printf("icmp6: multicast listener query ");
@@ -252,7 +296,7 @@ icmp6_print(const u_char *bp, const u_char *bp2)
if (vflag) {
#define RTSOLLEN 8
icmp6_opt_print((const u_char *)dp + RTSOLLEN,
- icmp6len - RTSOLLEN);
+ length - RTSOLLEN);
}
break;
case ND_ROUTER_ADVERT:
@@ -277,14 +321,14 @@ icmp6_print(const u_char *bp, const u_char *bp2)
printf("pref=%s, ",
get_rtpref(p->nd_ra_flags_reserved));
- printf("router_ltime=%d, ", ntohs(p->nd_ra_router_lifetime));
+ printf("router_ltime=%d, ", EXTRACT_16BITS(&p->nd_ra_router_lifetime));
printf("reachable_time=%u, ",
- (u_int32_t)ntohl(p->nd_ra_reachable));
+ EXTRACT_32BITS(&p->nd_ra_reachable));
printf("retrans_time=%u)",
- (u_int32_t)ntohl(p->nd_ra_retransmit));
+ EXTRACT_32BITS(&p->nd_ra_retransmit));
#define RTADVLEN 16
icmp6_opt_print((const u_char *)dp + RTADVLEN,
- icmp6len - RTADVLEN);
+ length - RTADVLEN);
}
break;
case ND_NEIGHBOR_SOLICIT:
@@ -297,7 +341,7 @@ icmp6_print(const u_char *bp, const u_char *bp2)
if (vflag) {
#define NDSOLLEN 24
icmp6_opt_print((const u_char *)dp + NDSOLLEN,
- icmp6len - NDSOLLEN);
+ length - NDSOLLEN);
}
}
break;
@@ -329,7 +373,7 @@ icmp6_print(const u_char *bp, const u_char *bp2)
}
#define NDADVLEN 24
icmp6_opt_print((const u_char *)dp + NDADVLEN,
- icmp6len - NDADVLEN);
+ length - NDADVLEN);
#undef NDADVLEN
}
}
@@ -339,22 +383,71 @@ icmp6_print(const u_char *bp, const u_char *bp2)
TCHECK(RDR(dp)->nd_rd_dst);
printf("icmp6: redirect %s",
getname6((const u_char *)&RDR(dp)->nd_rd_dst));
+ TCHECK(RDR(dp)->nd_rd_target);
printf(" to %s",
getname6((const u_char*)&RDR(dp)->nd_rd_target));
#define REDIRECTLEN 40
if (vflag) {
icmp6_opt_print((const u_char *)dp + REDIRECTLEN,
- icmp6len - REDIRECTLEN);
+ length - REDIRECTLEN);
}
break;
#undef REDIRECTLEN
#undef RDR
case ICMP6_ROUTER_RENUMBERING:
- icmp6_rrenum_print(icmp6len, bp, ep);
+ icmp6_rrenum_print(bp, ep);
break;
case ICMP6_NI_QUERY:
case ICMP6_NI_REPLY:
- icmp6_nodeinfo_print(icmp6len, bp, ep);
+ icmp6_nodeinfo_print(length, bp, ep);
+ break;
+ case ICMP6_HADISCOV_REQUEST:
+ printf("icmp6: ha discovery request");
+ if (vflag) {
+ TCHECK(dp->icmp6_data16[0]);
+ printf("(id=%d)", EXTRACT_16BITS(&dp->icmp6_data16[0]));
+ }
+ break;
+ case ICMP6_HADISCOV_REPLY:
+ printf("icmp6: ha discovery reply");
+ if (vflag) {
+ struct in6_addr *in6;
+ u_char *cp;
+
+ TCHECK(dp->icmp6_data16[0]);
+ printf("(id=%d", EXTRACT_16BITS(&dp->icmp6_data16[0]));
+ cp = (u_char *)dp + length;
+ in6 = (struct in6_addr *)(dp + 1);
+ for (; (u_char *)in6 < cp; in6++) {
+ TCHECK(*in6);
+ printf(", %s", ip6addr_string(in6));
+ }
+ printf(")");
+ }
+ break;
+ case ICMP6_MOBILEPREFIX_SOLICIT:
+ printf("icmp6: mobile router solicitation");
+ if (vflag) {
+ TCHECK(dp->icmp6_data16[0]);
+ printf("(id=%d)", EXTRACT_16BITS(&dp->icmp6_data16[0]));
+ }
+ break;
+ case ICMP6_MOBILEPREFIX_ADVERT:
+ printf("icmp6: mobile router advertisement");
+ if (vflag) {
+ TCHECK(dp->icmp6_data16[0]);
+ printf("(id=%d", EXTRACT_16BITS(&dp->icmp6_data16[0]));
+ if (dp->icmp6_data16[1] & 0xc0)
+ printf(" ");
+ if (dp->icmp6_data16[1] & 0x80)
+ printf("M");
+ if (dp->icmp6_data16[1] & 0x40)
+ printf("O");
+ printf(")");
+#define MPADVLEN 8
+ icmp6_opt_print((const u_char *)dp + MPADVLEN,
+ length - MPADVLEN);
+ }
break;
default:
printf("icmp6: type-#%d", dp->icmp6_type);
@@ -366,7 +459,7 @@ trunc:
}
static struct udphdr *
-get_upperlayer(u_char *bp, int *prot)
+get_upperlayer(u_char *bp, u_int *prot)
{
const u_char *ep;
struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
@@ -374,12 +467,13 @@ get_upperlayer(u_char *bp, int *prot)
struct ip6_hbh *hbh;
struct ip6_frag *fragh;
struct ah *ah;
- int nh, hlen;
+ u_int nh;
+ int hlen;
/* 'ep' points to the end of available data. */
ep = snapend;
- if (TTEST(ip6->ip6_nxt) == 0)
+ if (!TTEST(ip6->ip6_nxt))
return NULL;
nh = ip6->ip6_nxt;
@@ -404,7 +498,7 @@ get_upperlayer(u_char *bp, int *prot)
case IPPROTO_DSTOPTS:
case IPPROTO_ROUTING:
hbh = (struct ip6_hbh *)bp;
- if (TTEST(hbh->ip6h_len) == 0)
+ if (!TTEST(hbh->ip6h_len))
return(NULL);
nh = hbh->ip6h_nxt;
hlen = (hbh->ip6h_len + 1) << 3;
@@ -412,10 +506,10 @@ get_upperlayer(u_char *bp, int *prot)
case IPPROTO_FRAGMENT: /* this should be odd, but try anyway */
fragh = (struct ip6_frag *)bp;
- if (TTEST(fragh->ip6f_offlg) == 0)
+ if (!TTEST(fragh->ip6f_offlg))
return(NULL);
/* fragments with non-zero offset are meaningless */
- if ((fragh->ip6f_offlg & IP6F_OFF_MASK) != 0)
+ if ((EXTRACT_16BITS(&fragh->ip6f_offlg) & IP6F_OFF_MASK) != 0)
return(NULL);
nh = fragh->ip6f_nxt;
hlen = sizeof(struct ip6_frag);
@@ -423,7 +517,7 @@ get_upperlayer(u_char *bp, int *prot)
case IPPROTO_AH:
ah = (struct ah *)bp;
- if (TTEST(ah->ah_len) == 0)
+ if (!TTEST(ah->ah_len))
return(NULL);
nh = ah->ah_nxt;
hlen = (ah->ah_len + 2) << 2;
@@ -438,7 +532,7 @@ get_upperlayer(u_char *bp, int *prot)
return(NULL); /* should be notreached, though */
}
-void
+static void
icmp6_opt_print(const u_char *bp, int resid)
{
const struct nd_opt_hdr *op;
@@ -447,6 +541,7 @@ icmp6_opt_print(const u_char *bp, int resid)
const struct icmp6_opts_redirect *opr;
const struct nd_opt_mtu *opm;
const struct nd_opt_advinterval *opa;
+ const struct nd_opt_homeagent_info *oph;
const struct nd_opt_route_info *opri;
const u_char *cp, *ep;
struct in6_addr in6, *in6p;
@@ -505,9 +600,9 @@ icmp6_opt_print(const u_char *bp, int resid)
if (opp->nd_opt_pi_flags_reserved)
printf(" ");
printf("valid_ltime=%s,",
- get_lifetime((u_int32_t)ntohl(opp->nd_opt_pi_valid_time)));
+ get_lifetime(EXTRACT_32BITS(&opp->nd_opt_pi_valid_time)));
printf("preferred_ltime=%s,",
- get_lifetime((u_int32_t)ntohl(opp->nd_opt_pi_preferred_time)));
+ get_lifetime(EXTRACT_32BITS(&opp->nd_opt_pi_preferred_time)));
printf("prefix=%s/%d",
ip6addr_string(&opp->nd_opt_pi_prefix),
opp->nd_opt_pi_prefix_len);
@@ -531,7 +626,7 @@ icmp6_opt_print(const u_char *bp, int resid)
printf(")");
break;
}
- printf(" mtu=%u", (u_int32_t)ntohl(opm->nd_opt_mtu_mtu));
+ printf(" mtu=%u", EXTRACT_32BITS(&opm->nd_opt_mtu_mtu));
if (opm->nd_opt_mtu_len != 1)
printf("!");
printf(")");
@@ -541,10 +636,18 @@ icmp6_opt_print(const u_char *bp, int resid)
TCHECK(opa->nd_opt_adv_interval);
printf("(advint:"); /*)*/
printf(" advint=%u",
- (u_int32_t)ntohl(opa->nd_opt_adv_interval));
+ EXTRACT_32BITS(&opa->nd_opt_adv_interval));
/*(*/
printf(")");
- break;
+ break;
+ case ND_OPT_HOMEAGENT_INFO:
+ oph = (struct nd_opt_homeagent_info *)op;
+ TCHECK(oph->nd_opt_hai_lifetime);
+ printf("(ha info:"); /*)*/
+ printf(" pref=%d", EXTRACT_16BITS(&oph->nd_opt_hai_preference));
+ printf(", lifetime=%u", EXTRACT_16BITS(&oph->nd_opt_hai_lifetime));
+ printf(")");
+ break;
case ND_OPT_ROUTE_INFO:
opri = (struct nd_opt_route_info *)op;
TCHECK(opri->nd_opt_rti_lifetime);
@@ -569,7 +672,7 @@ icmp6_opt_print(const u_char *bp, int resid)
opri->nd_opt_rti_prefixlen);
printf(", pref=%s", get_rtpref(opri->nd_opt_rti_flags));
printf(", lifetime=%s",
- get_lifetime((u_int32_t)ntohl(opri->nd_opt_rti_lifetime)));
+ get_lifetime(EXTRACT_32BITS(&opri->nd_opt_rti_lifetime)));
/*(*/
printf(")");
break;
@@ -590,7 +693,7 @@ icmp6_opt_print(const u_char *bp, int resid)
#undef ECHECK
}
-void
+static void
mld6_print(const u_char *bp)
{
struct mld6_hdr *mp = (struct mld6_hdr *)bp;
@@ -602,7 +705,7 @@ mld6_print(const u_char *bp)
if ((u_char *)mp + sizeof(*mp) > ep)
return;
- printf("max resp delay: %d ", ntohs(mp->mld6_maxdelay));
+ printf("max resp delay: %d ", EXTRACT_16BITS(&mp->mld6_maxdelay));
printf("addr: %s", ip6addr_string(&mp->mld6_addr));
}
@@ -642,15 +745,17 @@ dnsname_print(const u_char *cp, const u_char *ep)
printf("\"");
}
-void
-icmp6_nodeinfo_print(int icmp6len, const u_char *bp, const u_char *ep)
+static void
+icmp6_nodeinfo_print(u_int icmp6len, const u_char *bp, const u_char *ep)
{
struct icmp6_nodeinfo *ni6;
struct icmp6_hdr *dp;
const u_char *cp;
- int siz, i;
+ size_t siz, i;
int needcomma;
+ if (ep < bp)
+ return;
dp = (struct icmp6_hdr *)bp;
ni6 = (struct icmp6_nodeinfo *)bp;
siz = ep - bp;
@@ -667,13 +772,13 @@ icmp6_nodeinfo_print(int icmp6len, const u_char *bp, const u_char *ep)
TCHECK2(*dp, sizeof(*ni6));
ni6 = (struct icmp6_nodeinfo *)dp;
printf(" ("); /*)*/
- switch (ntohs(ni6->ni_qtype)) {
+ switch (EXTRACT_16BITS(&ni6->ni_qtype)) {
case NI_QTYPE_NOOP:
printf("noop");
break;
case NI_QTYPE_SUPTYPES:
printf("supported qtypes");
- i = ntohs(ni6->ni_flags);
+ i = EXTRACT_16BITS(&ni6->ni_flags);
if (i)
printf(" [%s]", (i & 0x01) ? "C" : "");
break;
@@ -809,7 +914,7 @@ icmp6_nodeinfo_print(int icmp6len, const u_char *bp, const u_char *ep)
break;
}
- switch (ntohs(ni6->ni_qtype)) {
+ switch (EXTRACT_16BITS(&ni6->ni_qtype)) {
case NI_QTYPE_NOOP:
if (needcomma)
printf(", ");
@@ -822,7 +927,7 @@ icmp6_nodeinfo_print(int icmp6len, const u_char *bp, const u_char *ep)
if (needcomma)
printf(", ");
printf("supported qtypes");
- i = ntohs(ni6->ni_flags);
+ i = EXTRACT_16BITS(&ni6->ni_flags);
if (i)
printf(" [%s]", (i & 0x01) ? "C" : "");
break;
@@ -844,7 +949,7 @@ icmp6_nodeinfo_print(int icmp6len, const u_char *bp, const u_char *ep)
printf("\"");
} else
dnsname_print(cp, ep);
- if ((ntohs(ni6->ni_flags) & 0x01) != 0)
+ if ((EXTRACT_16BITS(&ni6->ni_flags) & 0x01) != 0)
printf(" [TTL=%u]", *(u_int32_t *)(ni6 + 1));
break;
case NI_QTYPE_NODEADDR:
@@ -857,7 +962,7 @@ icmp6_nodeinfo_print(int icmp6len, const u_char *bp, const u_char *ep)
break;
printf(" %s", getname6(bp + i));
i += sizeof(struct in6_addr);
- printf("(%d)", (int32_t)ntohl(*(int32_t *)(bp + i)));
+ printf("(%d)", (int32_t)EXTRACT_32BITS(bp + i));
i += sizeof(int32_t);
}
i = ni6->ni_flags;
@@ -889,8 +994,8 @@ trunc:
fputs("[|icmp6]", stdout);
}
-void
-icmp6_rrenum_print(int icmp6len, const u_char *bp, const u_char *ep)
+static void
+icmp6_rrenum_print(const u_char *bp, const u_char *ep)
{
struct icmp6_router_renum *rr6;
struct icmp6_hdr *dp;
@@ -901,6 +1006,8 @@ icmp6_rrenum_print(int icmp6len, const u_char *bp, const u_char *ep)
char hbuf[NI_MAXHOST];
int n;
+ if (ep < bp)
+ return;
dp = (struct icmp6_hdr *)bp;
rr6 = (struct icmp6_router_renum *)bp;
siz = ep - bp;
@@ -922,7 +1029,7 @@ icmp6_rrenum_print(int icmp6len, const u_char *bp, const u_char *ep)
break;
}
- printf(", seq=%u", (u_int32_t)ntohl(rr6->rr_seqnum));
+ printf(", seq=%u", EXTRACT_32BITS(&rr6->rr_seqnum));
if (vflag) {
#define F(x, y) ((rr6->rr_flags) & (x) ? (y) : "")
@@ -937,7 +1044,7 @@ icmp6_rrenum_print(int icmp6len, const u_char *bp, const u_char *ep)
printf("seg=%u,", rr6->rr_segnum);
printf("maxdelay=%u", rr6->rr_maxdelay);
if (rr6->rr_reserved)
- printf("rsvd=0x%x", (u_int16_t)ntohs(rr6->rr_reserved));
+ printf("rsvd=0x%x", EXTRACT_16BITS(&rr6->rr_reserved));
/*[*/
printf("]");
#undef F
@@ -1002,12 +1109,12 @@ icmp6_rrenum_print(int icmp6len, const u_char *bp, const u_char *ep)
printf("vltime=infty,");
else
printf("vltime=%u,",
- (u_int32_t)ntohl(use->rpu_vltime));
+ EXTRACT_32BITS(&use->rpu_vltime));
if (~use->rpu_pltime == 0)
printf("pltime=infty,");
else
printf("pltime=%u,",
- (u_int32_t)ntohl(use->rpu_pltime));
+ EXTRACT_32BITS(&use->rpu_pltime));
}
if (inet_ntop(AF_INET6, &use->rpu_prefix, hbuf,
sizeof(hbuf)))
OpenPOWER on IntegriCloud